Describing a world
Any domain can be written as config alone. Each entity has an initial_state, declarative fields, an optional machine that drives status, and an optional Python plugin for physics the rules can't express.
{
"domain": "smart_building",
"entities": [{
"id": "room", "type": "room", "count": 2,
"initial_state": {"temp_c": 24.0, "status": "cooling"},
"fields": {
"temp_c": {"behavior": "drift", "rate": -0.5, "when": "status == 'cooling'", "round": 1},
"power_kw": {"behavior": "derived", "expr": "3.5 if status == 'cooling' else 0.1"}
},
"machine": [{"source": "cooling", "target": "idle", "when": "temp_c <= 21"}]
}]
}
| Behaviour | Parameters | Each tick |
|---|---|---|
drift |
rate (units/s) |
x += rate·dt (rate 0 + noise = random walk) |
mean_revert |
target, strength (per s) |
moves toward target |
cycle |
base, amplitude, period_s, phase_s |
sine wave over sim time t |
derived |
expr |
computed from other fields |
Every field also takes min, max, noise (gaussian sd, scaled by √dt), round and when (it only updates while the expression is true). Fields update in order, so later ones can read earlier ones. Expressions can use state fields, t (sim seconds), tick, dt (tick length in seconds, so x + dt * rate integrates), arithmetic, comparisons, and/or/not, x if c else y, in, and min max abs round sqrt exp log sin cos clamp pi.
machine transitions are checked in order and the first match wins: source (a status, a list, or "*"), target, plus when and/or p (probability per second). A status change emits a state_changed event next to the regular telemetry_update.
count: N creates id-01 to id-N.
Built-in presets
| Domain | Prompt words | What runs |
|---|---|---|
| logistics | van, truck, fleet, delivery | vehicle plugin on real road routes (OSRM), battery drain, traffic delays, arrived / out_of_battery |
| devops | server, service, api, cluster | requests (daily cycle), CPU, memory leak, latency, error rate, healthy → degraded → restarting with restart count |
| iot | room, building, sensor, hvac | occupancy and weather daily cycles, temperature physics, CO₂, humidity, power, thermostat idle / cooling / heating |
| ecommerce | shop, store, checkout, cart | visitors, conversion, orders, revenue, payment failures, signups, inventory in_stock → low_stock → restocking |
| finance | stock, market, crypto, trading | market plugin: GBM price, order book, trades, volume spikes; tickers like BTC become entity ids |
Every preset except finance is pure config, and all of them can be overridden by POSTing your own config.
Prompt modifiers: a number or number word sets the count, and "10hz" sets the tick rate. Logistics: rain/snow/storm/fog and traffic raise the odds of delay, and "drain" speeds up battery loss. DevOps: "leak" and "spike". IoT: "heatwave", "cold". E-commerce: "black friday", "busy". Finance: "crypto", "volatile". The same seed gives the same trajectory.
Real roads
Logistics prompts name places: "3 vans from Southampton to Winchester" puts every van on that road route, "5 vans in rainy Bristol" sends each van on a random trip inside the city, and with no place named the city defaults to Southampton. Places are geocoded with OpenStreetMap Nominatim and routed with the public OSRM server once, when the simulation is created. Responses are cached in ~/.cache/vibesim (override with VIBESIM_CACHE_DIR), so the same prompt works offline afterwards. For heavy use, point VIBESIM_OSRM_URL / VIBESIM_NOMINATIM_URL at your own servers.