API Reference
LifeGrid provides a REST and WebSocket API built with FastAPI for programmatic access, streaming, and collaborative editing.
Starting the Server
uvicorn src.api.app:app --host 0.0.0.0 --port 8000 --reload
The API runs at http://localhost:8000. Interactive docs are available at /docs (Swagger UI) and /redoc.
REST Endpoints
Health Check
GET /health
Response: {"status": "ok"}
Create Session
POST /session
Creates a new simulation session.
Request body:
Field |
Type |
Default |
Description |
|---|---|---|---|
|
int (4–2048) |
64 |
Grid width |
|
int (4–2048) |
64 |
Grid height |
|
string |
|
Automaton mode |
|
string |
null |
Digit string of birth neighbour counts, e.g. |
|
string |
null |
Digit string of survival neighbour counts, e.g. |
|
string |
null |
Pattern name to load |
Response: {"session_id": "<uuid>"}
Example:
curl -X POST http://localhost:8000/session \
-H "Content-Type: application/json" \
-d '{"width": 128, "height": 128, "mode": "HighLife"}'
Step Session
POST /session/{session_id}/step
Advance the simulation by one or more generations.
Request body:
Field |
Type |
Default |
Description |
|---|---|---|---|
|
int (1–1000) |
1 |
Number of generations to advance |
Response: {"generation": <int>}
Get State
GET /session/{session_id}/state
Returns the current grid state.
Response:
{
"generation": 42,
"width": 128,
"height": 128,
"grid": [[0, 1, 0, ...], ...]
}
Load Pattern
POST /session/{session_id}/pattern
Load a pattern into the session grid.
Request body:
Field |
Type |
Description |
|---|---|---|
|
string |
RLE-encoded pattern string |
|
string |
Named pattern to load |
Provide either rle or pattern_name.
Response: {"status": "ok"}
Errors
All endpoints return 404 if the session ID is unknown.
WebSocket Endpoints
Simulation Stream
WS /session/{session_id}/stream
Connects to a simulation session and receives grid state frames automatically at approximately 20 Hz. Each frame is a JSON message with the same schema as the GET /session/{id}/state response.
Example (Python):
import asyncio
import websockets
import json
async def stream():
async with websockets.connect("ws://localhost:8000/session/<id>/stream") as ws:
async for message in ws:
data = json.loads(message)
print(f"Generation {data['generation']}, grid {data['width']}x{data['height']}")
asyncio.run(stream())
Collaborative Session
WS /collab/{session_id}?width=<int>&height=<int>
Multi-user collaborative editing. Multiple WebSocket clients share the same grid. The grid is created on demand with the specified dimensions (default 64x64).
Actions (client → server):
Action |
Payload |
Description |
|---|---|---|
|
|
Set a cell |
|
— |
Clear the grid and reset generation |
|
— |
Advance one Conway generation |
|
— |
Begin auto-stepping |
|
— |
Stop auto-stepping |
|
|
Set seconds between auto-steps (min 0.02) |
Message format (client → server):
{"action": "draw", "x": 10, "y": 15, "value": 1}
Broadcasts (server → all clients):
After every mutation the server broadcasts the full grid state to all connected clients:
{
"type": "state",
"generation": 5,
"grid": [[0, 1, ...], ...]
}
Example (Python):
import asyncio
import websockets
import json
async def collab():
uri = "ws://localhost:8000/collab/my-room?width=32&height=32"
async with websockets.connect(uri) as ws:
# Draw a cell
await ws.send(json.dumps({"action": "draw", "x": 5, "y": 5, "value": 1}))
# Receive broadcast
state = json.loads(await ws.recv())
print(f"Generation: {state['generation']}")
asyncio.run(collab())
Core Python API
Simulator
from src.core.config import SimulatorConfig
from src.core.simulator import Simulator
config = SimulatorConfig(width=100, height=100, automaton_mode="Conway's Game of Life")
sim = Simulator(config)
sim.initialize()
metrics = sim.step(num_steps=10) # Returns list of {generation, population, density}
grid = sim.get_grid() # numpy.ndarray
sim.set_cell(50, 50, 1)
sim.undo()
sim.redo()
summary = sim.get_metrics_summary() # {generations, current_population, max_population, ...}
Export Manager
from src.export_manager import ExportManager
em = ExportManager(theme="dark") # themes: "light", "dark", "blue", "warm"
# Single image — grid is the first positional argument
em.export_png(grid, "snapshot.png", cell_size=8)
# Animated GIF — add frames first, then export
for frame in frames:
em.add_frame(frame)
em.export_gif("animation.gif", cell_size=8, duration=100)
# Video
em.export_video("video.mp4", cell_size=8, fps=10, codec="mp4")
# JSON — filepath first, grid second
em.export_json("state.json", grid, metadata={"generation": 100})
RLE Parser
from src.advanced.rle_format import RLEParser, RLEEncoder
# Parse RLE string — returns (grid: np.ndarray, metadata: dict)
grid, metadata = RLEParser.parse("bo$2bo$3o!")
# Parse from file
grid, metadata = RLEParser.parse_file("pattern.rle")
# Encode grid to RLE string
rle_string = RLEEncoder.encode(grid)
# Encode directly to file
RLEEncoder.encode_to_file(grid, "output.rle")
Statistics
from src.advanced.statistics import StatisticsCollector
coll = StatisticsCollector(track_changes=True, calculate_entropy=False)
stats = coll.collect(step=1, grid=grid) # returns SimulationStatistics dataclass
print(stats.alive_cells, stats.density, stats.births, stats.deaths)
summary = coll.get_summary()
# {"total_steps": 1, "avg_density": 0.25, "avg_births": ..., ...}
For deeper per-grid metrics use EnhancedStatistics (all static methods):
from src.advanced.enhanced_statistics import EnhancedStatistics
entropy = EnhancedStatistics.calculate_entropy(grid)
complexity = EnhancedStatistics.calculate_complexity(grid, previous_grid)
Pattern Manager
from src.advanced.pattern_manager import PatternManager, PatternEntry
import numpy as np
pm = PatternManager(data_dir="user_data")
# Add a favourite
entry = PatternEntry.from_grid("my_glider", "conway", grid, description="A glider")
pm.add_favorite(entry)
# Retrieve all favourites
favs = pm.get_favorites()
# Search by tag
conway_patterns = pm.search_by_tag("conway")
# Search by name substring
results = pm.search_by_name("glider")
Cell Age Tracker
from src.advanced.cell_tracker import CellAgeTracker
tracker = CellAgeTracker(width=100, height=100)
tracker.update(grid) # call once per generation
ages = tracker.get_age_grid() # numpy array: 0 = dead, n = alive for n generations
AutoSave Manager
from src.autosave_manager import AutoSaveManager
am = AutoSaveManager(save_dir="autosave", interval=300, max_backups=5)
am.set_save_callback(lambda: {"generation": sim.generation, "grid": sim.get_grid().tolist()})
am.start()
# ... simulation runs ...
am.stop()
Plugin System
from src.plugin_system import PluginManager
pm = PluginManager()
count = pm.load_plugins_from_directory("plugins")
print(pm.list_plugins()) # ["Day & Night"]
automaton = pm.create_automaton("Day & Night", 100, 100)