Skip to content

The simulator

A 3D workbench where the program is the only thing you can get wrong

Blockly on the left, the arm and the head in the middle, an inspector on the right, and a log along the bottom. Everything below runs client-side; with no backend configured the app makes no network request at all.

The arm

Five joints, in a chain

Commands drive one joint at a time to an absolute angle. There is no inverse kinematics and no Cartesian move: if you want the tool somewhere, you work out the angles. That is the exercise.

Joint chain order, rotation axis, angle range, initial angle, and speed
JointAxisRangeInitialSpeed
Base YawbaseYawY-60° … 60°-45°60°/s
Shoulder RollshoulderRollX-45° … 45°0°45°/s
ShouldershoulderZ-20° … 100°45°45°/s
ElbowelbowZ-135° … 10°-80°60°/s
WristwristZ-100° … 100°35°75°/s

The chain nests in that order, so Shoulder Roll tilts everything below it out of the plane. A program that leaves it at zero can only work in one flat slice, which is why the starter program sets it to 15°.

The blocks

Three block types, and that is the whole language

Blockly emits a Program IR — never JavaScript. Your program is compiled and validated, not evaluated, so a block cannot reach the renderer, the hair state, or the score.

set <joint> to <angle>°

Drives one joint to an absolute angle. The joint list comes from the challenge, and an angle outside the joint’s range is rejected before the program runs.

wait <ms>

Holds the current pose. Accepts 0 to 5000 milliseconds.

repeat <n> times

Runs the blocks inside it 1 to 20 times. A loop costs one block instead of n, which is where most of the efficiency score is won.

Head clearance

A refusal, not a collision

The head is not made of voxels and cannot be cut. Before a command is committed, the tool’s path is swept continuously against the head geometry — a pure geometric constraint, not a physics engine with a tolerance that a fast move can tunnel through.

When the sweep finds contact, the run stops at the last safe pose, the source block that caused it is highlighted, and the simulation enters a recoverable error state. Nothing is silently corrected in the renderer: if the arm had to be stopped, you are told which block stopped it.

What the log says

12:04:31 run started · challenge neat-short-haircut

12:04:31 shoulderRoll → 15°

12:04:32 shoulder → 72°

12:04:33 elbow → 10° refused · head clearance · holding last safe pose

12:04:33 state → error (recoverable) · block 3

Scoring

What the score is made of

Three sub-scores, each clamped to 0–100, combined with fixed weights. The displayed number carries one decimal; full precision is kept internally.

Completion

×0.60

|Target ∩ Result| / |Target ∪ Result| × 100

Voxel IoU against the target hairstyle. Cutting too much costs exactly as much as cutting too little.

Efficiency

×0.25

min(100, ReferenceProgramCost / ProgramCost × 100)

Program cost is source blocks plus 0.25 per executed command, so a loop is cheaper than the same moves written out.

Time

×0.15

min(100, ReferenceTimeMs / EstimatedDurationMs × 100)

Estimated from joint travel and speed, never measured from the browser — a slow laptop cannot lose you points.

Final score

0.60 × Completion + 0.25 × Efficiency + 0.15 × Time

A provider that supplies weights not summing to 1 is rejected with an explainable error rather than quietly normalised.

Calibrated reference — Neat Short Haircut

Reference blocks5
Reference program cost6.25
Reference time5645 ms

The starter program scores at least 80 on completion and deliberately leaves room to improve. Matching it is not the ceiling.

Controls

Running a program

Run
Executes from the challenge’s initial state.
Pause / Resume
Stops and restarts the clock without losing the pose.
Step
Completes exactly one atomic command, from idle or paused.
Stop
Ends the run. No score is recorded — the panel shows a provisional completion only.
Reset
Restores the simulation and keeps your program.
Test
Evaluates the program headlessly, same engine and same result, in milliseconds.

Honestly

What is not built

Listing this is cheaper than letting someone discover it in a classroom.

  • Accounts and saved workspaces
  • Inverse kinematics or Cartesian moves
  • Scissor actuation or a physics engine
  • Realistic hair strands — hair is voxels
  • Robot self-collision
  • A dedicated mobile build
  • MQTT transport in the app (specified, not yet wired)
  • Production deployment

Questions

Asked often

Do we need to buy a robot?
No. The simulator is the product and it runs in a browser. The physical arm exists, and the firmware is open, but nothing on the teaching path depends on owning one.
What does it run on?
A desktop browser — Chrome or Edge — at roughly 1280×720 or larger. The workbench is 3D-first and assumes a keyboard and a pointer; there is no dedicated mobile build.
Can a student cheat the score?
In a versus round the program is replayed on the server and the score is the server’s. Nothing the browser reports is trusted, and the estimated execution time is computed from joint travel rather than measured, so hardware differences cannot move it.
Can the robot hit the head?
No. Head clearance is a geometric constraint checked by a continuous swept test, not a physics simulation with a tolerance. On contact the arm stops at its last safe pose and enters a recoverable error state.
Does it need an account?
Solo practice does not. Accounts and saved workspaces are not implemented yet — see what is not built.
Is it finished?
The client-side loop is complete and playable, along with the menu, solo mode and versus mode. Cross-browser visual acceptance is still open, and deployment has not started.