ScriptBots is a modern Rust reimagining of Andrej Karpathy’s classic agent-based evolution simulator. Our goal is a faithful, deterministic port with a GPU-accelerated UI, pluggable brain implementations, and first-class analytics. This is a multi-crate Cargo workspace separating simulation core, brains, storage, rendering, and the application shell.
For design intent and the living roadmap, see PLAN_TO_PORT_SCRIPTBOTS_TO_MODERN_IDIOMATIC_RUST_USING_GPUI.md (the project “bible”). A sibling WebAssembly plan lives in PLAN_TO_CREATE_SIBLING_APP_CRATE_TARGETING_WASM.md.
- Why this exists: ScriptBots is a minimalist artificial life laboratory. By rebuilding the original simulator with rigorously deterministic Rust systems, we can observe, measure, and reproduce emergent behavior at scale—without undefined behavior or global state muddying results.
- What we learn: How simple sensory channels and local rules produce complex population dynamics—cooperation vs. predation, resource gradients shaping migration, lineage divergence under different mutation schedules, and the role of perception in survival.
- LLM-in-the-loop science: The REST API, CLI, and MCP HTTP server expose the full control surface (knobs, patches, snapshots). This lets an external LLM agent act as an autonomous lab assistant: steering experiments, sweeping parameter spaces, logging observations into DuckDB, and drafting human-readable reports.
- Example workflows:
- Parameter sweeps: vary
mutation.{primary,secondary}and temperature gradients; record birth/death ratios and equilibrium populations. - Interventions: toggle
closedworlds, inject carnivore cohorts, or freeze food diffusion to test resilience. - Reporting: ingest DuckDB tables to auto-generate charts/tables describing discovered phenomena (e.g., altruistic giving thresholds that stabilize mixed diets).
- Parameter sweeps: vary
- Example workflows:
- A brain testbed: The
Braintrait and registry allow swapping decision engines—handwritten controllers, MLP/DWRAON/Assembly, or NeuroFlow—while holding the environment constant. This enables fair comparisons of:- Perception encoding (multi-eye vision, smell/sound/blood) and how architectures exploit them.
- Locomotion control (differential drive) and energy/health trade-offs.
- Evolutionary operators (mutation/crossover) and speciation pressures.
- Reproducible research: Deterministic pipelines + a growing replay roadmap mean results can be shared and re-run bit-for-bit, making the project a solid platform for pedagogy, papers, and benchmarking new brain designs.
- Determinism and safety: Replace legacy C++/GLUT and global state with idiomatic Rust, zero
unsafein v1, and reproducible runs. - Performance at scale: Data-parallelism (Rayon) and cache-friendly layouts to simulate thousands of agents efficiently.
- Modern UX: Declarative, GPU-accelerated GPUI interface with an inspector, overlays, and smooth camera controls.
- Observability: Persist metrics and snapshots to DuckDB for replay, analytics, and regression testing.
- Extensibility: Hot-swap brain implementations (MLP, DWRAON, experimental Assembly, plus optional NeuroFlow) without rewriting the world loop.
The workspace is organized for clear boundaries and fast incremental builds:
rust_scriptbots/
├── Cargo.toml # Workspace manifest, shared deps/lints/profiles
├── rust-toolchain.toml # Pinned toolchain (Rust 1.85)
├── crates/
│ ├── scriptbots-core # Simulation core (WorldState, AgentState, tick pipeline, config)
│ ├── scriptbots-brain # Brain trait + base implementations (mlp, dwraon, assembly)
│ ├── scriptbots-brain-ml # Optional ML backends (Candle/Tract/tch), feature-gated
│ ├── scriptbots-brain-neuro# NeuroFlow brain (optional), feature-gated
│ ├── scriptbots-index # Pluggable spatial indices (grid, rstar, kd-tree)
│ ├── scriptbots-storage # DuckDB-backed persistence & analytics hooks
│ ├── scriptbots-render # GPUI integration and visual layer (HUD, canvas renderer)
│ ├── scriptbots-app # Binary crate wiring everything together
│ └── scriptbots-web # Sibling WebAssembly harness (wasm-bindgen bindings; experimental)
└── docs/
└── wasm/ # ADRs, browser matrix, multithreading notes, rendering spikes
└── original_scriptbots_code_for_reference/ # Upstream C++ snapshot for parity
Data flows left-to-right; control surfaces are orthogonal and non-invasive:
┌───────────────────────────────────────┐
│ scriptbots-brain family │
│ (brain, brain-ml, brain-neuro) │
└──────────────┬────────────────────────┘
│ BrainRegistry (attach by key)
┌──────────────────────────────────────────▼──────────────────────────────────────────┐
│ scriptbots-core (WorldState, Tick Pipeline) │
│ - SoA AgentColumns · Spatial index (scriptbots-index) │
│ - Deterministic: sense → brains → actuation → persistence hooks │
└───────────────┬───────────────────────────┬───────────────────────────┬─────────────┘
│ AgentSnapshots │ PersistenceBatch │ CommandDrain (in-tick)
│ │ │
┌───────▼────────┐ ┌──────▼──────────┐ ┌─────▼──────────┐
│ Renderer (GUI) │ │ scriptbots- │ │ CommandBus │
│ GPUI window │ │ storage │ │ (crossfire MPMC)│
│ or Terminal TUI│ │ StoragePipeline│ └─────┬───────────┘
│ (console text) │ │ (async worker) │ │
└───────┬────────┘ └──────┬──────────┘ │
│ World snapshots └──────┬──────────┘ │
│ HUD metrics │ │
┌───────▼────────┐ │ │
│ scriptbots- │ ▼ │
│ render │ ┌────────────┐ │
└────────────────┘ │ DuckDB │ │
└────────────┘ │
│
┌─────────────────────────────────────────▼────────────────────┐
│ scriptbots-app (orchestrator) │
│ - launches ControlRuntime (Tokio thread) │
│ - selects Renderer (CLI flag/env) │
│ - seeds world, installs brains, primes history │
└───────────────┬───────────────────────────────┬──────────────┘
│ REST (axum + Swagger UI) │ MCP HTTP (mcp_protocol_sdk)
│ /api/knobs /api/config │ tools: list_knobs,get_config,
│ /api/knobs/apply PATCH config │ apply_updates,apply_patch
│ │
│ │
┌──────▼───────┐ │
│ control_cli │ (reqwest; TUI watch) │
│ list/get/set │ -> REST │
└──────────────┘ │
│
┌────────────────────────────────────────────────────────────────────────────────▼─────────────┐
│ scriptbots-web (wasm) │
│ - wasm-bindgen: default_init_options/init_sim/tick/snapshot/reset/registerBrain │
│ - snapshot_format: json | binary (Postcard) · wasm-vs-native parity tests │
│ - feeds JS renderer (WebGPU/Canvas) │
└──────────────────────────────────────────────────────────────────────────────────────────────┘
- Background workers: StoragePipeline (async writer) and ControlRuntime (Tokio) are isolated; the core drains commands inside the tick loop for deterministic application.
- Renderers are read-only consumers of world snapshots; they do not mutate simulation state directly.
- Control surfaces are transport-agnostic; both REST and MCP use the same safe
ControlHandleand enqueue commands with back-pressure.
scriptbots-core: Simulation core withWorldState,AgentState, deterministic staged tick pipeline, config, sensor/actuation scaffolding, and brain registry bindings.scriptbots-brain:Braintrait + baseline implementations and adapters; experimentalassemblybehind a feature.scriptbots-brain-ml: Optional ML backends (Candle, Tract, tch) for alternative/accelerated inference (feature-gated).scriptbots-brain-neuro: Optional NeuroFlow-based brain; controllable at runtime via config/env (see below).scriptbots-index: Spatial indexing implementations; default uniform grid, optionalrstar(R-tree) andkd(kiddo).scriptbots-storage: DuckDB persistence with buffered writes (ticks,metrics,events,agents) plus analytics helpers (e.g.,top_predators,latest_metrics).scriptbots-render: GPUI UI layer with a window shell, HUD, canvas renderer for agents/food, selection highlights, and diagnostics overlay.scriptbots-app: Binary shell. Wires tracing/logging, config/env, storage pipeline, installs brains, seeds agents, and launches the GPUI shell.scriptbots-web: WebAssembly harness exposing bindings to init/tick/reset and snapshot the simulation; consumesscriptbots-corewithdefault-features = false(sequential fallback; Rayon disabled on wasm).
- Workspace scaffolding, shared lints, and profiles are in place.
scriptbots-core: World state, agent runtime, staged tick, reproduction/combat hooks, history summaries, and brain registry integration are implemented; parity tasks are tracked in the plan doc.scriptbots-render: GPUI window + HUD + canvas renderer with camera controls, selection highlights, and diagnostics overlay; audio is optional viakirafeature.scriptbots-brain:MlpBrainavailable; experimentalassemblyfeature; DWRAON planned; registry wiring present.scriptbots-brain-neuro: NeuroFlow-backed brain available behind theneurofeature (runtime toggles below).scriptbots-storage: DuckDB persistence with buffered writes and analytics helpers.
See the migration roadmap in PLAN_TO_PORT_SCRIPTBOTS_TO_MODERN_IDIOMATIC_RUST_USING_GPUI.md for staged milestones and parity checklists.
- Rust toolchain: pinned in
rust-toolchain.toml(Rust 1.85). Install viarustup. - OS: Linux, macOS, or Windows 11 (native or WSL2). GPU drivers should be up to date for best GPUI performance (wgpu backends: Metal/macOS, Vulkan/Linux, D3D12 or Vulkan/Windows).
cargo checkCPU tuning note: Workspace builds now default to a portable baseline so CI runners don’t require AVX2/“native” features. Set
RUSTFLAGS="-C target-cpu=native"locally (all launch scripts already do this) if you want host-specific tuning.
cargo run -p scriptbots-app- Threads: By default, the core auto-budgets worker threads conservatively. Our profiling shows best throughput at 8 threads on a 32-core CPU for this workload. To match that:
SCRIPTBOTS_MAX_THREADS=8 cargo run -p scriptbots-app -- --storage memory --storage-thresholds 128,4096,1024,1024- With servers disabled (avoid port conflicts/background overhead):
SCRIPTBOTS_CONTROL_REST_ENABLED=false \
SCRIPTBOTS_CONTROL_MCP=disabled \
SCRIPTBOTS_MAX_THREADS=8 \
cargo run -p scriptbots-app -- --mode terminal --storage memory --storage-thresholds 128,4096,1024,1024- Profiling helpers (headless):
# No storage (isolates world.step performance)
SCRIPTBOTS_MAX_THREADS=8 cargo run -p scriptbots-app -- --profile-steps 1000
# With storage (memory) and tuned flush thresholds
SCRIPTBOTS_MAX_THREADS=8 cargo run -p scriptbots-app -- --profile-storage-steps 3000 --storage memory --storage-thresholds 128,4096,1024,1024Set logging verbosity with RUST_LOG, for example:
RUST_LOG=info cargo run -p scriptbots-app-
Force the emoji TUI renderer (useful on headless machines):
SCRIPTBOTS_MODE=terminal cargo run -p scriptbots-app
-
Auto fallback:
SCRIPTBOTS_MODE=auto(default) will drop into terminal mode if no GUI backend is available (e.g., SSH sessions). -
Override detection:
SCRIPTBOTS_FORCE_TERMINAL=1→ force terminal even when a display server is present.SCRIPTBOTS_FORCE_GUI=1→ keep GPUI even if no display variables are set (may still fail if the OS truly lacks a GUI).
-
CI/headless smoke runs can bypass raw TTY requirements by setting
SCRIPTBOTS_TERMINAL_HEADLESS=1, which drives the renderer against an in-memory buffer for a few frames. -
Emoji mode (terminal renderer):
- Defaults ON when a modern UTF‑8 terminal is detected; press
eto toggle at runtime. - Force enable via env:
SCRIPTBOTS_TERMINAL_EMOJI=1|true|yes|on; force disable with0|false|off|no. - Heuristic: enabled if
TERMis notdumb/linux/vt100, locale containsutf-8|utf8, andCIis unset. - Emoji mappings: terrain
🌊/💧/🏜/🌿/🌺/🪨(lush swaps:🐟,🌴,🌾, barren🥀); agents single🐇/🦝/🦊, small groups🐑/🐻/🐺, large cluster👥, boosted🚀, spike peak⚔(underline). Heading arrows remain for single agents when available. - If emojis render as tofu/misaligned, install an emoji-capable font (e.g., Noto Color Emoji) or toggle off with
e.
- Defaults ON when a modern UTF‑8 terminal is detected; press
-
Narrow symbols mode: press
nto switch to width-1 friendly symbols while keeping emoji colors off-background; helpful for strict terminals/alignment. -
Headless report (CI-friendly):
SCRIPTBOTS_MODE=terminal \ SCRIPTBOTS_TERMINAL_HEADLESS=1 \ SCRIPTBOTS_TERMINAL_HEADLESS_FRAMES=24 \ SCRIPTBOTS_TERMINAL_HEADLESS_REPORT=terminal_report.json \ cargo run -p scriptbots-app -- --storage memory --threads 2
This renders offscreen for N frames and writes a JSON summary (frames, ticks, births/deaths, energy stats) to
terminal_report.json.
Use the convenience scripts in the repo root to launch ScriptBots with sensible defaults per OS. These scripts set appropriate targets, isolate build artifacts, and pick the right renderer.
- Script:
run_linux_terminal_mode.sh - Usage:
chmod +x ./run_linux_terminal_mode.sh ./run_linux_terminal_mode.sh
- What it does:
- Detects CPU count into
THREADS(nproc/getconffallback; override by exportingTHREADSbeforehand) - Builds with native CPU optimizations (
RUSTFLAGS="-C target-cpu=native") - Forces terminal renderer (
SCRIPTBOTS_MODE=terminal) - Runs release binary with cargo job parallelism
-j $THREADSand passes--threads $THREADSto the app
- Detects CPU count into
- Customize:
- Reduce CPU usage:
THREADS=2 ./run_linux_terminal_mode.sh - Headless CI snapshot: export
SCRIPTBOTS_TERMINAL_HEADLESS=1to render against an in-memory buffer - Logging:
RUST_LOG=info ./run_linux_terminal_mode.sh
- Reduce CPU usage:
- Script:
run_linux_with_bevy.sh - Usage:
chmod +x ./run_linux_with_bevy.sh ./run_linux_with_bevy.sh
- What it does:
- Detects CPU count (caps default at 8) and passes the value to cargo (
-j) and the app (--threads) - Prefers Vulkan via
WGPU_BACKEND=vulkan, falling back to GL when Vulkan is unavailable - Enables the Bevy renderer (
--features bevy_render) and launches with--mode bevy - Sets high-performance WGPU hints (
SB_WGPU_PRESENT_MODE=full, bloom/tonemap/fog defaults) matching the Windows helper
- Detects CPU count (caps default at 8) and passes the value to cargo (
- Customize:
- Limit load:
THREADS=4 ./run_linux_with_bevy.sh - Force GL:
WGPU_BACKEND=gl ./run_linux_with_bevy.sh - Append extra flags after the final
--(e.g.,./run_linux_with_bevy.sh -- --debug-watermark)
- Limit load:
- Script:
run_macos_version_with_console.sh - Usage:
chmod +x ./run_macos_version_with_console.sh ./run_macos_version_with_console.sh
- What it does:
- Detects arch (
arm64vsx86_64) and sets--targetaccordingly - Isolates artifacts per-arch via
CARGO_TARGET_DIR=target-macos-$ARCH - Unsets any stray cross-compile/link flags for a clean native build
- Uses all cores for build jobs and launches the app in terminal mode (
--mode terminal)
- Detects arch (
- Customize:
- Add app flags by appending to the final
-- ...section (e.g.,--threads 8) - Override logging:
RUST_LOG=info ./run_macos_version_with_console.sh
- Add app flags by appending to the final
- Script:
run_macos_version_with_gui.sh - Usage:
chmod +x ./run_macos_version_with_gui.sh ./run_macos_version_with_gui.sh
- What it does:
- Same target/artifact isolation as console script
- Prefers Metal backend for
wgpu(WGPU_BACKEND=metal) - Builds with
--features guiand launches GUI mode (--mode gui) using--threads 8
- Customize:
- Tune threads: edit
--threads 8or setSCRIPTBOTS_MAX_THREADSenv - Troubleshoot rendering: you can add
--renderer-safeto the app args if you see a black canvas
- Tune threads: edit
- Script:
run_macos_version_with_bevy.sh - Usage:
chmod +x ./run_macos_version_with_bevy.sh ./run_macos_version_with_bevy.sh
- What it does:
- Selects the correct target triple (
aarch64-apple-darwinon Apple Silicon,x86_64-apple-darwinotherwise) - Isolates build artifacts per-arch and clears stray cross-compilation flags
- Forces the Metal backend, high-performance power preference, and Bevy feature flag (
--features bevy_render) - Launches ScriptBots with
--mode bevyand a default 8-thread budget (override withTHREADSenv)
- Selects the correct target triple (
- Customize:
- Retina tweaks: set
SB_WGPU_RES_SCALEto0.5or2.0before running - Lower CPU use:
THREADS=4 ./run_macos_version_with_bevy.sh - Add Bevy-specific CLI flags after
--, e.g.,./run_macos_version_with_bevy.sh -- --dump-bevy-png docs/rendering_reference/golden/bevy_default.png
- Retina tweaks: set
- Script:
run_windows_version_with_console.bat - Usage:
- Double-click in Explorer, or run from a Developer PowerShell/Command Prompt:
run_windows_version_with_console.bat
- Double-click in Explorer, or run from a Developer PowerShell/Command Prompt:
- What it does:
- Uses MSVC target
x86_64-pc-windows-msvc - Isolates artifacts under
target-windows-msvc - Uses all cores for build jobs and launches terminal mode (
--mode terminal)
- Uses MSVC target
- Prereqs:
- Rust MSVC toolchain and Visual Studio Build Tools (Windows 11 SDK) installed
- Script:
run_windows_version_with_gui.bat - Usage:
- Double-click in Explorer, or run from a Developer PowerShell/Command Prompt:
run_windows_version_with_gui.bat
- Double-click in Explorer, or run from a Developer PowerShell/Command Prompt:
- What it does:
- Same MSVC target/artifact isolation as console script
- Builds with
--features guiand launches GUI mode (--mode gui) using--threads 8
- Customize:
- Adjust threads by editing the
--threadsvalue; add app flags after--as needed (e.g.,--debug-watermark)
- Adjust threads by editing the
- Script:
run_windows_version_with_bevy.bat - Usage:
- Double-click in Explorer, or run from a Developer PowerShell/Command Prompt:
run_windows_version_with_bevy.bat
- Double-click in Explorer, or run from a Developer PowerShell/Command Prompt:
- What it does:
- Reuses the MSVC target (
x86_64-pc-windows-msvc) with isolated artifacts undertarget-windows-msvc - Sets high-performance WGPU hints (
WGPU_BACKEND=Vulkan,WGPU_POWER_PREFERENCE=high_performance) - Builds with
--features bevy_renderand launches the Bevy renderer (--mode bevy) using--threads 8
- Reuses the MSVC target (
- Customize:
- To force D3D12 instead of Vulkan: set
set WGPU_BACKEND=d3d12before running - Add Bevy-only flags (e.g.,
--dump-bevy-png) after the final--in the script
- To force D3D12 instead of Vulkan: set
Notes (all platforms):
- The final
-- ...segment in each script passes flags to the application binary. You can add flags like--storage memory,--profile-steps 1000, or--det-check 200there. - To stream control API docs, ensure REST is enabled (default) and open
http://127.0.0.1:8088/docswhile the app runs.
rustup target add wasm32-unknown-unknown
cargo check --target wasm32-unknown-unknown -p scriptbots-web- Install Rust (MSVC toolchain):
- Download
rustup-init.exeand select the MSVC target, or run in PowerShell:
rustup default stable-x86_64-pc-windows-msvc rustup component add clippy rustfmt
- Download
- Install Visual Studio Build Tools (2022+):
- Select the "Desktop development with C++" workload (includes MSVC, Windows 10/11 SDK).
- Update GPU drivers (NVIDIA/AMD/Intel) to latest. Ensure D3D12 is available; Vulkan runtime optional.
- Build and run:
cargo run -p scriptbots-app
- Troubleshooting: If linking fails with MSVC or SDK errors, re-run the VS installer to include the Windows 11 SDK and C++ toolset (v143+).
- Windows 11 with WSLg supports Linux GUI apps out of the box; GPUI rendering generally works, but performance may vary. If you see blank windows, update your GPU drivers and WSL kernel, then retry.
scriptbots-appfeatures:ml→ enablescriptbots-brain-mlneuro→ enablescriptbots-brain-neurofast-alloc→ enable mimalloc as the global allocator for improved multithreaded performance- Example:
cargo run -p scriptbots-app --features neuro - Note: default features enable
ml,neuro, andfast-alloc. To disable defaults, use--no-default-featuresand opt-in explicitly.
scriptbots-render:audio→ enable Kira-driven audio in the UI layer
scriptbots-index(pluggable spatial indices):- Default:
grid; optional:rstar,kd - Example:
cargo build -p scriptbots-index --features rstar
- Default:
scriptbots-brain-ml(optional ML backends):candle,tract,tch(all optional)- Examples:
cargo build -p scriptbots-brain-ml --features candlecargo build -p scriptbots-brain-ml --features tractcargo build -p scriptbots-brain-ml --features tch
Note: App-level switches for brain/index selection are wired in the binary; crate-specific features control availability.
If built with the neuro feature, runtime toggles can be applied via env vars before launch:
SCRIPTBOTS_NEUROFLOW_ENABLED=true \
SCRIPTBOTS_NEUROFLOW_HIDDEN="64,32,16" \
SCRIPTBOTS_NEUROFLOW_ACTIVATION=relu \
cargo run -p scriptbots-app --features neuroValid activations: tanh, sigmoid, relu.
# Build the whole workspace
cargo build --workspace
# Run the UI shell
cargo run -p scriptbots-app
# Lint and format
cargo clippy --workspace --all-targets --all-features
cargo fmt --all
# Run tests (as they land)
cargo test --workspace
# Build optional crates with features
cargo build -p scriptbots-index --features rstar
cargo build -p scriptbots-brain-ml --features candle--mode {auto|gui|terminal}: select renderer. Defaults toautoand can be set viaSCRIPTBOTS_MODE.auto: use GPUI when a display is detected; otherwise fall back to terminal.gui: force GPUI; may fail on headless systems.terminal: force emoji TUI.
--dump-png <FILE>(GUI builds): write an offscreen PNG and exit (no UI). Pair with--png-size WxH.--png-size WxH(GUI builds): snapshot size for--dump-png(e.g.,1280x720).--debug-watermark: overlay a tiny diagnostics watermark in the render canvas.--renderer-safe: force a conservative paint path (useful for troubleshooting black canvas on some Windows setups).--threads N: cap simulation worker threads (overrides low-power defaults).--low-power: prefer lower CPU usage (equivalent to--threads 2unless--threadsis provided); also biasesautotoward terminal.--profile-steps N: headlessworld.step()profiling without persistence.--profile-storage-steps N: headless profiling with selected storage mode.--storage-thresholds t,a,e,m: override flush thresholds (tick, agent, event, metric).--profile-sweep N: run a sweep of configurations for profiling and print a summary.--auto-tune N: quick sweep to pick threads/thresholds for the chosen storage, then continue.--det-check N: run determinism self-check (1-thread vs N-threads summaries comparison).--dump-png FILE+--png-size WxH(GUI builds): write an offscreen PNG and exit.--storage {duckdb|memory}: select persistence backend;memoryuses an in-memory DuckDB for analytics without disk I/O.- Auto-pause (any renderer):
--auto-pause-below COUNT(orSCRIPTBOTS_AUTO_PAUSE_BELOW) pauses when population ≤ COUNT--auto-pause-age-above AGE(orSCRIPTBOTS_AUTO_PAUSE_AGE_ABOVE) pauses when any agent’s age ≥ AGE--auto-pause-on-spike(orSCRIPTBOTS_AUTO_PAUSE_ON_SPIKE=true) pauses on first spike hit event
RUST_LOG— logging filter (e.g.,info,trace,scriptbots_core=debug).RAYON_NUM_THREADS— set simulation thread pool size whenparallelis enabled.SCRIPTBOTS_MODE—auto|gui|terminal(renderer selection).SCRIPTBOTS_FORCE_TERMINAL/SCRIPTBOTS_FORCE_GUI— hard override renderer detection (1|true|yes).SCRIPTBOTS_TERMINAL_HEADLESS— render TUI to an in-memory buffer for CI smoke tests.SCRIPTBOTS_TERMINAL_HEADLESS_FRAMES— number of frames to render in headless mode (default 12; max 360).SCRIPTBOTS_TERMINAL_HEADLESS_REPORT— file path to write a JSON summary from a headless run.SCRIPTBOTS_MAX_THREADS— preferred maximum thread budget; core will cap Rayon to min of CPUs and this value (used unlessRAYON_NUM_THREADSis already set).SCRIPTBOTS_TERMINAL_EMOJI— force emoji mode1|true|yes|onor disable with0|false|off|no.SCRIPTBOTS_RENDER_SAFE— force conservative rendering path in GUI mode (also enabled by--renderer-safeor--low-power).SCRIPTBOTS_RENDER_WATERMARK— overlay a tiny diagnostics watermark in the GUI canvas (also enabled by--debug-watermark).SCRIPTBOTS_NEUROFLOW_ENABLED—true|false.SCRIPTBOTS_NEUROFLOW_HIDDEN— comma-separated hidden sizes (e.g.,64,32,16).SCRIPTBOTS_NEUROFLOW_ACTIVATION—tanh|sigmoid|relu.SCRIPTBOTS_CONTROL_REST_ADDR— REST bind address (default127.0.0.1:8088).SCRIPTBOTS_CONTROL_SWAGGER_PATH— Swagger UI path (default/docs).SCRIPTBOTS_CONTROL_REST_ENABLED—true|false.SCRIPTBOTS_CONTROL_MCP—disabled|http(defaulthttp).SCRIPTBOTS_CONTROL_MCP_HTTP_ADDR— MCP HTTP bind address (default127.0.0.1:8090).SCRIPTBOTS_STORAGE_PATH— DuckDB file path for persistence (defaultscriptbots.db; set to a unique path per run during experiments).
- On capable desktops, ScriptBots opens two GPUI windows: a canvas window rendering the world and a HUD window with controls, charts, and inspector. If a second window cannot be created (WM limits/remote desktop), the app falls back to a single-window overlay layout automatically.
Deterministic, staged tick pipeline (seeded RNG; stable ordering):
- Aging and scheduled tasks
- Food respawn/diffusion
- Reset runtime flags
- Sense (spatial index snapshot)
- Brain tick (no per-tick allocs)
- Actuation (double-buffered state)
- Food intake/sharing (deterministic reductions)
- Combat and death (queued → commit)
- Reproduction (mutation/crossover) in stable order
- Persistence hooks (batched to DuckDB)
- Zero undefined behavior: no
unsafein v1; clear ownership and lifetimes. - Stable order of effects: floating-point reductions and removals are staged and applied in a fixed order for bitwise-stable runs across thread counts.
- Per-agent RNG: seeds derive from a global seed +
AgentId, keeping behavior stable as populations change and threads vary. - Feature-gated parallelism:
scriptbots-coredefaults toparallel(Rayon), while web builds disable it for single-thread determinism.
- Set a fixed seed in config:
rng_seed = <u64>. At runtime you can apply via REST:{ "rng_seed": 42 } - For CPU thread control during profiling, prefer the standard
RAYON_NUM_THREADSenv var.
- SoA layout: agents use cache-friendly columns (
AgentColumns) for fast scans during sense/actuation. - Generational IDs: slotmap-backed
AgentIdprevents stale references and enables stable iteration. - Spatial index: uniform hash grid by default (opt-in
rstar/kd). Sense builds a read-only snapshot; actuation writes into a double buffer to avoid races.
- Sensors: multi-eye vision cones (angular), smell/sound/blood channels with attenuation, temperature discomfort, and clock/age cues.
- Outputs: differential drive (wheel velocities), color/indicator pulses, spike length easing, give intent (altruistic food sharing), boost control, sound output.
- Mapping: outputs drive physics and side-effects (e.g., spike damage scales with spike length and speed) and are logged for analytics.
- Brain trait with
tick/mutate/crossover; implementations include MLP (production),dwraon(feature),assembly(experimental). - Brain registry: per-run registry attaches runners by key, enabling hybrid populations and runtime selection. Random spawns draw from
BrainRegistry::random_keyfor mixed-species runs; sexual crossover is gated to same-kind brains (species barrier). Brains can optionally expose activation snapshots for visualization. - Genome & genetics: genomes capture topology/activations; mutation/crossover create hybrid births with lineage tracking and tests.
- NeuroFlow (optional): deterministic CPU MLP with runtime toggles; seed-stable outputs verified in tests.
- Food dynamics: configurable growth, decay, diffusion, and fertility capacity; speed-based intake and reproduction bonuses mirror legacy behavior.
- Topography: tile-based terrain/elevation influence fertility and movement energy (downhill momentum/energy costs).
- Temperature: gradient and per-agent preference drive discomfort drains; exposed in config and analytics.
- Closed worlds & seeding: enforce closed ecosystems; maintain population floors and scheduled spawns.
- The core ships a rule-based Wave Function Collapse (WFC) generator that produces deterministic terrain (
TerrainLayer) and optional fertility/temperature fields from a tileset spec. This enables quick scenario bootstrapping and repeatable experiments. - Status and usage live in the plan doc; upcoming surfaces include REST/CLI endpoints to generate/apply artifacts. The hydrology system builds atop terrain for dynamic water flows (see below).
- Runtime hydrology state models per-cell flow direction, accumulation, basins, and a water depth field. Use
GET /api/hydrologyfor a snapshot with:width,height,total_water_depth,mean_water_depth, flooded cell counts with thresholds- Arrays:
water_depth,flow_directions(N/S/E/W/-),basin_ids,accumulation,spill_elevation
- CLI:
scriptbots-control hydrologyprints a summary (ratios, thresholds, array sizes). Hydrology integrates with terrain and is deterministic per seed.
- Spikes: damage scales with requested spike length and agent speed; collision resolution is staged for determinism.
- Carcass sharing: meat distribution honors age scaling and diet tendencies; events persisted for analysis.
- Analytics: attacker/victim flags (carnivore/herbivore), births/deaths, hybrid markers, age/boost tracking, and per-tick summaries feed the HUD and DuckDB.
- GPUI window, HUD, and canvas renderer for food tiles and agents (circles/spikes). Dual-window layout opens a HUD window and a simulation canvas window; a single-window overlay fallback is used when needed.
- Camera controls: pan/zoom; keyboard bindings for pause, draw toggle, speed ±.
- Overlays: selection highlights, diagnostics panel; charts and advanced overlays are staged in the plan.
- Functional search: the settings panel includes a live search bar that filters parameters across all categories via a centralized filter, making it fast to find and tweak knobs.
- Inspector: per-agent stats and genome/brain views (scoped to plan milestones); mutation-rate adjusters (±) for primary/secondary let you tweak an agent’s evolution parameters live.
- Optional audio via
kira(featureaudio).
- Adaptive GPU adapter selection; viewport culling for terrain; chart decimation; batched path rendering to reduce draw calls.
- Troubleshooting flags:
--renderer-safe(conservative paint path) and--debug-watermark(tiny on-canvas badge) help isolate rendering issues.
- Colorblind-safe palettes (deuteranopia/protanopia/tritanopia) and a high-contrast mode; UI elements and overlays respect palette transforms.
- Keyboard remapping with conflict resolution and capture mode; discoverable bindings in the HUD.
- Narration hooks prepared for future screen-reader integration; toggles surfaced in the inspector.
- The app selects a
Rendererimplementation at runtime (gpuiorterminal) via--mode {auto|gui|terminal}or environment variables. Both renderers consume the same world snapshots and control bus.
- Playback:
spacepause/resume,+/-speed up/down,ssingle-step - Views:
dtoggle drawing,ftoggle food overlay,Ctrl+Shift+Otoggle agent outlines - Spawning:
aadd crossover agents,q/hspawn carnivore/herbivore - World:
ctoggle closed environment,ofollow oldest,sfollow selected - Accessibility:
pcycle color palettes (with keyboard rebinding support)
- Optional
kira-backed mixer (featureaudio) with event-driven cues (births, deaths, spikes) and accessibility toggles. - Channels planned for ambience/effects; platform caveats apply on Linux/WSL2. Audio is disabled in wasm; use Web Audio API from JS if needed.
An emoji-rich terminal renderer is planned behind a terminal feature/CLI mode (--mode {auto|gui|terminal}) with fallback when GPUI cannot start. See the “Terminal Rendering Mode (Emoji TUI)” section in PLAN_TO_PORT_SCRIPTBOTS_TO_MODERN_IDIOMATIC_RUST_USING_GPUI.md.
-
Force the emoji TUI renderer (useful on headless machines):
SCRIPTBOTS_MODE=terminal cargo run -p scriptbots-app
-
Auto fallback:
SCRIPTBOTS_MODE=auto(default) will drop into terminal mode if no GUI backend is available (e.g., SSH sessions). -
Override detection:
SCRIPTBOTS_FORCE_TERMINAL=1→ force terminal even when a display server is present.SCRIPTBOTS_FORCE_GUI=1→ keep GPUI even if no display variables are set (may still fail if the OS truly lacks a GUI).
-
CI/headless smoke runs can bypass raw TTY requirements by setting
SCRIPTBOTS_TERMINAL_HEADLESS=1, which drives the renderer against an in-memory buffer for a few frames. -
Emoji mode (terminal renderer):
- Defaults ON when a modern UTF‑8 terminal is detected; press
eto toggle at runtime. - Force enable via env:
SCRIPTBOTS_TERMINAL_EMOJI=1|true|yes|on; force disable with0|false|off|no. - Heuristic: enabled if
TERMis notdumb/linux/vt100, locale containsutf-8|utf8, andCIis unset. - Emoji mappings: terrain
🌊/💧/🏜/🌿/🌺/🪨(lush swaps:🐟,🌴,🌾, barren🥀); agents single🐇/🦝/🦊, small groups🐑/🐻/🐺, large cluster👥, boosted🚀, spike peak⚔(underline). Heading arrows remain for single agents when available. - If emojis render as tofu/misaligned, install an emoji-capable font (e.g., Noto Color Emoji) or toggle off with
e.
- Defaults ON when a modern UTF‑8 terminal is detected; press
-
Narrow symbols mode: press
nto switch to width-1 friendly symbols while keeping emoji colors off-background; helpful for strict terminals/alignment.
Keybinds: space (pause), +/- (speed), s (single-step), b (toggle metrics baseline), S (save ASCII screenshot), e (emoji), n (narrow symbols), x (expanded panels), ?/h (help), q/Esc (quit). The terminal HUD shows tick/agents/births/deaths/energy, Insights (rolling metrics), Mortality panel, Brains leaderboard, recent events log, and an emoji world mini-map. The layout is responsive and auto-expands panels on wider terminals; press x to toggle. Screenshots saved via S are written under screenshots/frame_<tick>.txt.
- DuckDB schema (
ticks,metrics,events,agents) with buffered writes and maintenance (optimize,VACUUM). - Analytics helpers:
latest_metrics,top_predators. - Deterministic replay tooling is planned in the roadmap.
- Tables created automatically on first run:
ticks(tick, epoch, closed, agent_count, births, deaths, total_energy, average_energy, average_health)metrics(tick, name, value)(primary key(tick,name))events(tick, kind, count)(primary key(tick,kind))agents(tick, agent_id, generation, age, position_x, position_y, velocity_x, velocity_y, heading, health, energy, color_r, color_g, color_b, spike_length, boost, herbivore_tendency, sound_multiplier, reproduction_counter, mutation_rate_primary, mutation_rate_secondary, trait_smell, trait_sound, trait_hearing, trait_eye, trait_blood, give_intent, brain_binding, food_delta, spiked, hybrid, sound_output, spike_attacker, spike_victim, hit_carnivore, hit_herbivore, hit_by_carnivore, hit_by_herbivore)
- Example queries:
-- Latest metrics snapshot select m.name, m.value from metrics m where m.tick = (select max(tick) from metrics) order by name; -- Top predators by average energy select agent_id, avg(energy) as avg_energy, max(spike_length) as max_spike_length from agents group by agent_id order by avg_energy desc limit 10;
Configuration: persistence buffers flush automatically; call optimize() periodically in long sessions (the app pipeline already triggers maintenance).
Population trend (10-tick moving average):
with ticks as (
select tick, agent_count, row_number() over(order by tick) as rn
from ticks
)
select t1.tick,
avg(t2.agent_count) as population_ma10
from ticks t1
join ticks t2 on t2.rn between t1.rn-9 and t1.rn
group by t1.tick
order by t1.tick;Kill ratios (carnivore vs herbivore):
select sum(case when hit_carnivore then 1 else 0 end) as carnivore_hits,
sum(case when hit_herbivore then 1 else 0 end) as herbivore_hits
from agents;Energy histogram at latest tick:
with latest as (select max(tick) as t from agents)
select width_bucket(energy, 0, 2.0, 20) as bucket,
count(*)
from agents, latest
where agents.tick = latest.t
group by bucket
order by bucket;- Schema compatibility: additive changes only until v1; breaking changes guarded behind feature flags and migration scripts.
- Maintenance: the storage worker batches inserts and exposes
optimize()/VACUUMhooks; long sessions should calloptimize()periodically (the app pipeline already schedules it).
- Coding standards: See
RUST_SYSTEM_PROGRAMMING_BEST_PRACTICES.md. EmbraceResult-based errors, clear traits, and avoidunsafe. - Linting:
cargo clippy --workspace --all-targets --all-features -W clippy::all -W clippy::pedantic -W clippy::nursery - Formatting:
cargo fmt --all - Tests:
cargo test --workspace(simulation and GPUI tests will be added as systems land) - Profiles: Release uses LTO, single codegen unit, and abort-on-panic for optimal binaries.
- Core tests: unit and property tests for reproduction math, spike damage, food sharing/consumption; determinism tests run seeded scenarios and assert stable summaries.
- Render tests: GPUI compile-time view tests; terminal HUD headless smoke tests (
SCRIPTBOTS_TERMINAL_HEADLESS=1). - Benchmarks:
criterionharness for ticks/sec at various agent counts. - CI: matrix for macOS 14 and Ubuntu 24.04; wasm job builds
scriptbots-web, runs parity tests in headless Chromium; release job usescargo distwith optional macOS codesigning.
- CPU profiling (Linux/macOS): run with
RUSTFLAGS='-g'and useperf record/perf reportordtrace/Instruments; annotate hot paths in sense/actuation. - Tracy (optional): integrate client in dev builds to visualize frame times and background worker activity.
- Threading: tune
RAYON_NUM_THREADSto match physical cores; verify determinism with seeded runs. - Rendering: measure HUD/canvas frame times; avoid per-frame allocations; prefer batched path building.
- Built-in tools:
--profile-steps Nand--profile-storage-steps Nto run headless micro-benchmarks--profile-sweep Nand--auto-tune Nto explore and auto-pick thread/flush settings--renderer-safefor a conservative paint path;--debug-watermarkoverlays a diagnostics badge
- Low-power mode: in addition to capping threads (
--low-power), the app lowers OS process priority (Unix niceness +10; Windows BELOW_NORMAL) to be a better background citizen.
- Logging uses
tracingwithRUST_LOGfilters (e.g.,RUST_LOG=info,scriptbots_core=debug). - Categories of interest:
scriptbots_core::world— tick summaries, seeding, closed/open flipsscriptbots_storage— flushes, optimize/vacuumscriptbots_app::servers— REST and MCP server lifecycle, tool invocationsscriptbots_render— window lifecycle, input bindings
- Prefer structured fields (e.g.,
tick = summary.tick.0) for machine-readable logs. Avoid panics in production; release profile usespanic = abort.
- Default address:
http://127.0.0.1:8088(overrideSCRIPTBOTS_CONTROL_REST_ADDR) - Swagger UI path:
/docs(overrideSCRIPTBOTS_CONTROL_SWAGGER_PATH) - OpenAPI JSON:
/api-docs/openapi.json - Enable/disable:
SCRIPTBOTS_CONTROL_REST_ENABLED=true|false - Endpoints:
GET /api/knobs→ list flattened config knobsGET /api/config→ fetch entire config snapshotPATCH /api/config→ apply JSON object patch{ ... }POST /api/knobs/apply→ apply list of{ path, value }updatesGET /api/ticks/latest→ latest tick summary (JSON)GET /api/ticks/stream→ server-sent events stream of tick summaries (SSE)GET /api/ticks/ndjson→ newline-delimited JSON stream of tick summaries (NDJSON)GET /api/screenshot/ascii→ ASCII snapshot of terminal mini-map (text/plain)GET /api/screenshot/png→ offscreen PNG snapshot (requires GUI feature)GET /api/hydrology→ hydrology snapshot (flow directions, accumulation, basins) if availableGET /api/events/tail→ recent events (birth/death/combat) ring bufferGET /api/scoreboard→ top carnivores and oldest agents at a glanceGET /api/agents/debug→ lightweight agent debug table (filters: ids, diet, selection, brain)POST /api/selection→ queue a selection update (modes: set/add/clear; optional state: none|hovered|selected)GET /api/presets→ list scenario presetsPOST /api/presets/apply→ apply preset by nameGET /api/config/audit→ recent config patches (audit ring buffer)
Examples:
# Filter debug table by selected agents only, limit 20, sort by age
curl -s 'http://127.0.0.1:8088/api/agents/debug?selection=selected&limit=20&sort=age' | jq .
# Select a cohort (replace selection with ids [1,2,3])
curl -s -X POST http://127.0.0.1:8088/api/selection \
-H 'content-type: application/json' \
-d '{"mode":"set","agent_ids":[1,2,3]}'
# Add agents to current selection and mark them highlighted
curl -s -X POST http://127.0.0.1:8088/api/selection \
-H 'content-type: application/json' \
-d '{"mode":"add","agent_ids":[4,5],"state":"highlighted"}'More examples:
# List and apply a preset via REST
curl -s http://127.0.0.1:8088/api/presets | jq
curl -s -X POST http://127.0.0.1:8088/api/presets/apply -H 'content-type: application/json' -d '{"name":"arctic"}' | jq .
# Recent events (tail)
curl -s 'http://127.0.0.1:8088/api/events/tail?limit=10' | jq .
# Scoreboard (top predators, oldest agents)
curl -s 'http://127.0.0.1:8088/api/scoreboard?limit=10' | jq .
# SSE tick stream (press Ctrl+C to stop)
curl -N -H 'Accept: text/event-stream' http://127.0.0.1:8088/api/ticks/stream | sed -n '1,10p'REST quickstart:
# 1) Start the app
cargo run -p scriptbots-app
# 2) Open Swagger UI in a browser
# http://127.0.0.1:8088/docs
# 3) List knobs
curl -s http://127.0.0.1:8088/api/knobs | jq '.[0:10]'
# 4) Patch configuration (enable NeuroFlow, set layers, activation)
curl -s -X PATCH http://127.0.0.1:8088/api/config \
-H 'content-type: application/json' \
-d '{"patch":{"neuroflow":{"enabled":true,"hidden_layers":[64,32,16],"activation":"relu"}}}' | jq .
# 5) Apply typed updates
curl -s -X POST http://127.0.0.1:8088/api/knobs/apply \
-H 'content-type: application/json' \
-d '{"updates":[{"path":"food_max","value":0.6}]}' | jq .
# 6) Stream ticks as NDJSON (Ctrl+C to stop)
curl -s http://127.0.0.1:8088/api/ticks/ndjson | head -n 5
# 7) Take an ASCII screenshot (text) and a PNG (requires GUI feature)
curl -s http://127.0.0.1:8088/api/screenshot/ascii > frame.txt
curl -s http://127.0.0.1:8088/api/screenshot/png > frame.pngExample PATCH body:
{ "food_max": 0.6, "neuroflow": { "enabled": true, "hidden_layers": [64,32,16], "activation": "relu" } }- Points to the REST API (default
SCRIPTBOTS_CONTROL_URL=http://127.0.0.1:8088):
cargo run -p scriptbots-app --bin control_cli -- list
cargo run -p scriptbots-app --bin control_cli -- get
cargo run -p scriptbots-app --bin control_cli -- set neuroflow.enabled true
cargo run -p scriptbots-app --bin control_cli -- patch --json '{"food_max":0.6}'
cargo run -p scriptbots-app --bin control_cli -- watch --interval-ms 750
cargo run -p scriptbots-app --bin control_cli -- export metrics --db scriptbots.db --last 1000 --out latest_metrics.csv
# New commands:
cargo run -p scriptbots-app --bin control_cli -- presets
cargo run -p scriptbots-app --bin control_cli -- apply-preset arctic
cargo run -p scriptbots-app --bin control_cli -- screenshot --out screenshots/frame_0001.txt
cargo run -p scriptbots-app --bin control_cli -- screenshot --png --out screenshots/frame_0001.png
cargo run -p scriptbots-app --bin control_cli -- hydrologyInteractive dashboard:
# Live TUI dashboard of knobs and their current values; press 'q' to quit, 'r' to refresh
cargo run -p scriptbots-app --bin control_cli -- watch --interval-ms 500- Layered configs: pass one or more
--config path/to/file.toml(or.ron) flags—or setSCRIPTBOTS_CONFIGwith semicolon-separated paths—to build scenarios from reusable fragments (e.g.,base.toml → arctic_biome.toml → evolution_study.toml). Layers merge in order before env overrides, unlocking repeatable experiments without editing code. - Config inspection: add
--print-configto dump the merged configuration (default JSON) or--write-config output.tomlto persist it; choose--config-format json|toml|ronand combine with--config-onlyfor a dry run in CI/tooling workflows. - Replay verification:
cargo run -p scriptbots-app -- --replay-db run.duckdb [--compare-db candidate.duckdb] [--tick-limit 500]loads persisted events, re-simulates ticks headlessly, and reports colored diffs (tick/sequence mismatches, event payload divergences) together with event-type counts. - Storage helpers: DuckDB accessors (
max_tick,load_replay_events,replay_event_counts) underpin the CLI so analytics pipelines or external tools can reuse the same deterministic data.
- Default:
127.0.0.1:8090over HTTP; disable withSCRIPTBOTS_CONTROL_MCP=disabled. - Override bind address:
SCRIPTBOTS_CONTROL_MCP_HTTP_ADDR=127.0.0.1:9090. - Tools exposed:
list_knobs→ returns array of knob entriesget_config→ returns full config snapshotapply_updates→ accepts{ updates: [{ path, value }, ...] }apply_patch→ accepts{ patch: { ... } }Notes: Only HTTP transport is supported here; stdio/SSE are not used.
MCP quickstart:
- Start the app; verify MCP binds on
127.0.0.1:8090(override viaSCRIPTBOTS_CONTROL_MCP_HTTP_ADDR). - Connect an MCP HTTP client to the endpoint; available tools:
list_knobs,get_config,apply_updates,apply_patch. - Each tool returns structured JSON; use your MCP-compatible agent to orchestrate parameter sweeps and log findings to DuckDB.
- Current state: all configuration changes flow through the control surfaces (REST, MCP HTTP, CLI). Values persist only for the lifetime of the session unless you export them yourself.
- Planned: upcoming releases will load layered configuration files (TOML/RON) so experiments can stack reusable building blocks (e.g., base world defaults → biome overrides → study-specific tweaks).
base.toml → arctic_biome.toml → evolution_study.toml - Workaround today: capture the desired configuration via the REST API (
GET /api/config), version it externally, and apply deltas back withPATCH /api/configor the CLI. - Value: researchers gain repeatable, composable scenarios without hand-editing large monolithic files, making it easy to swap biomes, mutation parameters, or analytics presets on demand.
- Already implemented
- ✅ Event-log schema (
replay_eventstable) storing every tick’s RNG scope, brain outputs, and actions. - ✅ Type-safe encoding for replay artifacts shared by storage and analytics crates.
- ✅ Storage plumbing to persist replay batches alongside standard metrics.
- ✅ Event-log schema (
- Planned
- ❌ Headless replay runner capable of deterministic re-simulation from stored events.
- ❌ Branch/diff workflows comparing Rust vs. Rust PR builds vs. the legacy C++ baseline.
- ❌ CLI tooling that surfaces divergence reports and snapshot diffs.
- ❌ DuckDB-backed analysis views for quick triage of regressions and experiment outcomes.
- Use cases
- Parity testing between the Rust port and the original C++ implementation.
- Regression prevention by replaying critical seeds in CI or pre-merge checks.
- Debugging elusive bugs by reproducing exact agent decisions tick-by-tick.
- Long-running research experiments that demand bitwise-stable replays.
- REST and MCP servers bind to loopback by default. If you expose them externally, front with TLS and configure CORS appropriately. The WASM path requires COOP/COEP headers only when enabling multithreading; single-thread builds avoid this.
All configuration can be inspected and updated at runtime via REST/CLI/MCP. Common knobs:
- World:
world_width,world_height,closed - Population:
population_minimum,population_spawn_interval - Food:
food_max,food_regrowth_rate,food_diffusion,food_decay,fertility_strength - Temperature:
temperature_gradient,temperature_offset - Reproduction:
reproduction_rate_carnivore,reproduction_rate_herbivore,mutation.{primary,secondary} - Traits:
trait_modifiers.{smell,sound,hearing,eye,blood} - NeuroFlow:
neuroflow.{enabled,hidden_layers,activation}
Use GET /api/knobs to discover the full flattened list with current values.
Runtime constraints:
- Changing
world_width/world_heightat runtime is rejected; restart with new dimensions. - Some composite changes may be coerced (e.g., number/string parsing) but type mismatches are rejected with a clear error.
- The app owns a bounded MPMC
CommandBus; external surfaces (REST, MCP, CLI) enqueueControlCommands. - The simulation drains the queue inside the tick loop before state mutation, guaranteeing coherent updates and avoiding data races.
- Back-pressure: when the queue is full, commands are rejected with a clear error; clients should retry with jitter.
- Keep changes scoped to the relevant crate; prefer improving existing files over adding new ones unless functionality is genuinely new.
- Update docs where it helps future maintainers understand decisions and invariants.
- For larger tasks, update
PLAN_TO_PORT_SCRIPTBOTS_TO_MODERN_IDIOMATIC_RUST_USING_GPUI.mdinline to mark progress.
We maintain a sibling browser-targeted crate, scriptbots-web, that reuses core crates without invasive changes. See PLAN_TO_CREATE_SIBLING_APP_CRATE_TARGETING_WASM.md and docs/wasm/ (ADRs, audits, capability matrix). Initial MVP runs single-threaded by disabling scriptbots-core’s parallel feature on wasm; WebGPU vs Canvas2D rendering is under evaluation.
Quick peek:
crates/scriptbots-web/web/ships a Canvas demo harness that consumes the wasm snapshots, surfaces live metrics, and can be served locally viapython -m http.server. Binary snapshots (snapshot_format: "binary") and custom seeding strategies are already wired in for experimentation.
Helpful docs:
docs/wasm/adrs/ADR-001-wasm-rendering.md— rendering stack decision recorddocs/wasm/adrs/ADR-002-browser-persistence.md— browser persistence approachdocs/wasm/adrs/ADR-004-component-model.md— component model/WASI Preview assessmentdocs/wasm/browser_matrix.csv— browser capabilities (WebGPU, SAB, SIMD)
snapshot_format:json(default) orbinary(PostcardUint8Array).- APIs:
default_init_options(),init_sim(options),tick(steps),snapshot(),reset(seed?),registerBrain("wander"|"mlp"|"none"). - Determinism: wasm-vs-native parity tests compare snapshots for fixed seeds; single-thread fallback is default (Rayon disabled).
- For multithreading (future), browsers require SharedArrayBuffer with headers:
Cross-Origin-Opener-Policy: same-originCross-Origin-Embedder-Policy: require-corp
- Local dev: serve with a static server that sets these headers (or use a service worker). For now, single-thread builds avoid the requirement.
- CI: the wasm job runs parity tests in headless Chromium; see
.github/workflows/ci.yml.
Licensed under MIT OR Apache-2.0 (see workspace manifest).
- Original ScriptBots by Andrej Karpathy (reference snapshot included under
original_scriptbots_code_for_reference/). - This Rust port is an independent, from-scratch implementation guided by parity goals and modern Rust/GPUI best practices.
- What platforms are supported? Linux, macOS, and Windows 11 are targeted. Windows is supported natively (MSVC toolchain) and via WSL2. Early UI milestones may see platform-specific polish arriving at different times.
- Where do I start hacking?
scriptbots-corefor the world model;scriptbots-renderfor the GPUI view;scriptbots-brainfor brain interfaces;scriptbots-storagefor persistence.
- MSVC/SDK link errors on Windows: Ensure VS Build Tools "Desktop development with C++" and Windows 11 SDK are installed. Then run
rustup default stable-x86_64-pc-windows-msvc. - Blank or crashing window: Update GPU drivers. On WSL2, update the WSL kernel and try again. Verify that your system supports D3D12 (Windows) or Vulkan/Metal (Linux/macOS).
- DuckDB file lock errors: Close any external tools accessing the DB file and retry. Prefer unique DB paths per run while developing.
- Determinism regressions: Ensure you haven't introduced unordered parallel reductions; stage results and apply in a stable commit phase.
- Releases are built via
cargo distin therelease-buildsGitHub Actions workflow. Publish a new version by tagging the repository (git tag v0.x.y && git push origin v0.x.y) or running the workflow manually with ataginput. - The workflow produces archives for Linux (
x86_64-unknown-linux-gnu), Windows (x86_64-pc-windows-msvc), and a universal macOS build (Apple Silicon + Intel). Artifacts are uploaded as workflow run assets for review before attaching them to a GitHub Release. - macOS codesigning: provide the following repository secrets for automatic signing (the job skips codesign if they are absent):
MACOS_CERT_BASE64: base64-encoded.p12Developer ID certificate.MACOS_CERT_PASSWORD: password used to protect the.p12.MACOS_SIGNING_IDENTITY: e.g."Developer ID Application: Example Corp (TEAMID1234)".MACOS_KEYCHAIN_PASSWORD(optional): override keychain password used on the runner.
- Certificates are imported into a temporary keychain, the binaries (and
.appbundles when present) are signed with the supplied identity, and the archives are repackaged. Add notarization credentials later if we adopt automated notarization. - Release operators should verify the uploaded artifacts locally (
codesign --verify --deepon macOS,shasum -a 256on every platform) before drafting public releases.
- Core data structures and config (done); expand parity (metabolism, locomotion, food math, carcass sharing).
- World mechanics and determinism under parallelism; spatial index tuning.
- Brains: MLP shipped; DWRAON + Assembly (feature-gated) and NeuroFlow optional.
- Storage: extend analytics, add replay hooks and regression tests.
- Rendering: HUD/overlays/inspector polish; performance diagnostics.
- Packaging/CI: release builds, binaries; wasm sibling crate scaffolding (non-invasive).
- The app now registers multiple brain families by default (MLP, DWRAON, Assembly experimental, NeuroFlow) and seeds mixed populations automatically. Random spawns are bound to a sampled brain family.
- NeuroFlow is enabled in the default config; edit at runtime via REST/CLI or env (e.g., SCRIPTBOTS_NEUROFLOW_*).
- Sexual reproduction only occurs within the same brain kind (species barrier). Cross-kind parents fall back to random spawns. This allows fair A/B comparisons between families.
- To force a single brain for new agents, bind a chosen
brain_keyto agents viaWorldState::bind_agent_brainor modify the seeding function to always pick that key.