Cellcode Studio

Factory cell simulator

Cellcode

Write a factory process in Cellcode, a small YAML language for humanoid work. The compiler turns it into a timeline of walks, picks, machine cycles and handoffs. The figures then play that timeline with no keyframes. Every pose is solved live from springs, two-bone IK and footsteps planned ahead on the timeline, 120 times a second.

Edit the settings on the right. The timeline recompiles as you type.
Compiling
Cycle time–until the last figure finishes
Output–items delivered to bins
Throughput–per minute
Bottleneck–busiest station
CrewNowBusyWaited
0.0 / 0.0 s
Click a clip to jump to it and to its line in the program.
Walk Carry Work: pick, place, operate, inspect Gesture Blocked Planned wait Station running ◇ Signal

Field notes

The math under each pose

Each figure is a handful of small solvers stacked on top of the timeline. The four figures below are live: drag and slide to see the same code that drives the studio.

Fig. A · Second-order dynamicsEvery joint target goes through a spring

Targets from the timeline jump: a hand is at rest, then it should be at the shelf. A second-order system turns that jump into motion. Three numbers describe how it feels: f is the natural frequency in hertz, ζ is damping (below 1 it overshoots) and r is the initial response (above 1 it anticipates, below 0 it winds up first). The body uses a calm spring, the gaze a quick one with r above 1 so the head leads the turn.

y + k₁·ẏ + k₂·ÿ = x + k₃·ẋ k₁ = ζ / (π f) k₂ = 1 / (2π f)² k₃ = r ζ / (2π f) stable step: k₂' = max(k₂, h²/2 + h·k₁/2, h·k₁)
2.2 Hz
0.60
0.5

Fig. B · Two-bone IK, law of cosinesArms and legs solve in closed form

An arm or leg is two bones of fixed length. The triangle they make with the target has three known sides, so the elbow angle falls out of the law of cosines with no iteration. A pole point picks which way the joint bends: in front of the knee, behind and outside the elbow. Drag the target. When it is out of reach the chain straightens and points at it.

d = |T − S|, cos α = (a² + d² − b²) / (2 a d) elbow = S + a·(cos α · û + sin α · ŵ) û = (T − S)/d, ŵ = normalize(pole − S − ((pole − S)·û) û)

Fig. C · Gait from the timelineFeet land where the hips will be

Steps alternate at the rig's cadence. Each swing lasts part of the step interval, so both feet are down for a moment between swings. When a foot lifts off, the planner asks the timeline where the pelvis will be at the middle of that foot's next stance. It lands there, which is the Raibert placement rule, read from the future instead of predicted. Hip height comes from the stance leg itself, so the body dips when both feet are down.

landing = p(t + T_swing + T_stance/2) + side · hip_width hip_y = min(stand, ankle_y + √(L² − d²)) (inverted pendulum) T_step = 1/cadence, T_swing = 0.8·T_step, stride = speed / cadence
1.9 steps/s
0.72 m
80%

Fig. D · The null vectorHow the impossible stair closes

Axonometric projection flattens three dimensions into two, so one direction disappears. For the 30° view that direction is (1, 1, 1). A cube moved along it stays put on screen while the side elevation shows it climbing. The Penrose ring in the studio is a real helix that rises by k levels and shifts k bays in x and z. That shift is k·(1, 1, 1), so the end draws exactly on top of the start. Rotate the studio view and the seam opens.

P = ⎡ cos30° 0 −cos30° ⎤ P · (1, 1, 1)ᵀ = 0 ⎣ ½ −1 ½ ⎦ ring sides: n−1, n−1, n−1−k, n−1−k → end − start = k·(1, 1, 1)

Arc lengthPaths walk at a steady speed

A* on the bay grid gives tile centres. Corners are rounded by Chaikin cutting, then the path is indexed by distance so speed never depends on how many points a segment has. A trapezoid profile ramps speed in and out over 0.35 s.

T = L / v + t_ramp s(u) = v̂u²/2a u < a = v̂(u − a/2) a ≤ u ≤ 1−a

Reach budgetLift with the legs

For each pick or place the compiler searches hip hinge and knee crouch together, scoring how far the shoulder ends from a comfortable reach. Crouching costs more than hinging, but a deep hinge costs more than both, so low shelves get a squat and benches get a bow.

cost = 10·(|S(θ,c) − T| − 0.8·arm)² + 0.35·θ + 0.9·c + 2·max(0, θ − 0.7)²

Gaze hierarchyEyes lead, spine follows

The head chases its look target through a fast, lightly damped spring with r above 1. The torso yaw follows the path tangent through a slower one. The lag between them reads as intent: the figure looks at the press before it turns to walk there.

head: f 3.5 Hz, ζ 0.4, r 1.5 body: f 2.2 Hz, ζ 0.6, r 0.5 hands: f 3.6 Hz, ζ 0.75, r 0

Counter-rotationArms swing against legs

The pelvis yaws toward the leading leg and the shoulders turn the other way. Each hand swings opposite the same-side foot, scaled by that foot's forward offset from the pelvis. There is no arm-swing curve anywhere; it comes out of where the feet are.

pelvis_yaw = 0.18 · (f_R − f_L) / stride hand_fwd = −0.3 · f_side

Discrete eventsThe timeline is a simulation

Cellcode is compiled by running it. Each figure has a program counter. A step either runs, taking time, or blocks on a station lock, an empty shelf, a full bench or a signal. Every state change wakes the blocked figures in first-come order. Blocked time becomes the dashed clips on the timeline.

pick blocks until item ∧ ¬locked place blocks until count + incoming < capacity wait consumes one signal token

Deterministic replayScrub anywhere

Machine cycle jitter comes from a seeded generator, so the same source always compiles to the same timeline. The rig steps at a fixed 120 Hz and snapshots its state every second. Scrubbing restores the nearest snapshot and simulates forward, so a pose at 42.3 s is the same whether you played or jumped there.

x ← x + 0x6D2B79F5 (mulberry32) h = 1/120 s, snapshot every 1.0 s
ProblemUsed in CellcodeMathAlternatives worth knowingWhy this choice
Limb IKAnalytic two-bone with polelaw of cosinesFABRIK (the robot's arms), CCD, Jacobian transpose / damped least squaresExact, branch-free, and knees never flip. FABRIK is better for longer chains like spines and tails.
SmoothingSecond-order dynamics (f, ζ, r)y + k₁ẏ + k₂ÿ = x + k₃ẋExponential lerp, critically damped spring, PD controllersThree designer-readable numbers cover lag, bounce and anticipation.
SteppingPhase-locked steps with timeline look-aheadRaibert placementThreshold stepping (robot arm), motion matching, physics-based controllersThe timeline knows the future, so feet land exactly on corners and stairs.
Body heightStance-leg constraint√(L² − d²)Authored bob curves, spring-mass (SLIP) modelsThe walking dip falls out of the leg geometry and scales with stride.
Secondary motionSprings on hands, gaze, crouchsemi-implicit EulerVerlet + distance constraints (the robot's antenna), position-based dynamicsHumanoids need little jiggle; springs give weight shifts and follow-through.
SchedulingDiscrete-event simulationpriority queue + wakeupsPetri nets, behaviour trees, PDDL plannersReadable in YAML, deterministic, and reports waits and bottlenecks directly.
Projection30° axonometric, painter's sortP·(1,1,1) = 0Perspective (robot arm), orthographic planArchitectural convention; measurable; allows Escher geometry.

Language reference

Cellcode 0.1

A Cellcode file is YAML with units and bay references. It has two surfaces: the YAML you write and the JavaScript builder it compiles to (see the compiled.js tab). Both describe the same cell and produce the same timeline.

Top-level keyWhat it holds
cellcode cell seed shiftVersion, cell name, random seed for jitter, and the simulation horizon (default 600 s). repeat: forever runs until the horizon.
worldgrid: [cols, rows] of 1 m bays, platforms (at, size, level), stairs (at, dir, run), and one penrose ring (at, size, rise).
itemsItem name → shape: cube, disc, crate, sphere, rod.
stationsName → at, kind (rack machine bench bin), face (side the figure stands on), stock, capacity, and for machines makes: a -> b and cycle.
rigsName → height, gait (cadence, stride, lift), and springs body, gaze, hands as { f, zeta, r }. A rig called figure exists by default.
crewName → rig, at, and any rig field to override for that figure.
programCrew name → list of steps. Every crew member runs its list in parallel from t = 0.
ValueForms
BayJ2 is column J, row 2. Columns run A→ along x (east), rows 1→ along z (south). [9, 1] is the same bay as zero-based numbers.
Time4s, 400ms, 1.5min, and jitter 4s ± 0.6s (or +-), drawn uniformly from the seed.
Length1.75m, 72cm, or a bare number in metres.
Frequency1.9hz or a bare number.
Directionnorth (−z), south (+z), east (+x), west (−x).
SignalAny dotted name, like gear.ready. Each signal adds a token; each wait takes one.
StepFormTakesBlocks untilThe figure
walk / runwalk: press · run: C4distance ÷ speed + 0.35 s–Plans an A* route across bays and stairs; heads along the tangent.
pickpick: gear from press1.4 san unreserved item is there and the machine is idleWalks there first if needed, then solves a hinge and crouch to reach.
placeplace: qa1.2 sthe station has roomCarries with both hands, lowers the item to the station.
operateoperate: press · for: 3sthe machine's cyclethe machine is free and holds its inputWorks the panel; a machine with makes converts its item at the end.
inspectinspect: 2sas given–Raises the held item to eye level and turns it.
waitwait: gear.ready · wait: 2sas givena token for that signalCrosses its arms, shifts weight, watches.
signalsignal: gear.ready0.5 s–Taps the air; a ring pulses at its head.
ascend / descendascend: penrose · laps: 2laps × ring length ÷ speed–Walks the impossible stair forever up (or down).
repeatrepeat: 3 · do: [ … ]––forever repeats until the shift horizon.
Gestureswave · nod · stretch · point: press · look: qa · idle: 2s0.8–3 s–Short readable signals between tasks.

Why YAMLReadable on a shop floor

A process engineer should be able to read a cell without learning a programming language. YAML gives indentation for nesting, lists for sequences and maps for options. Cellcode adds units, bay references and verbs. The parser keeps a line number for every value, so errors, timeline clips and the running-line markers in the editor all point back to your source.

Why a JS surfaceGenerate cells in code

Large factories are generated, not typed. The compiled.js tab shows the same program as a chainable builder in plain units (seconds, metres, hertz). Use it to stamp out ten cells from one template, sweep crew sizes, or feed an optimiser. The YAML is the file you share; the builder is the API.