TrainsCoding

How a run works

Inside a run

What exactly happens between pushing the throttle and pulling it back. This page is for teachers who want to explain surprising behaviour, and for developers working on the engine. The rules are deterministic: the same program, with the same random numbers, always does the same thing.

Design and run are separate

A project is a design: characters, their placements and their trains. Pressing ▶ takes a snapshot of the design and runs that. Nothing a running program does is ever written back to the project. Stopping throws the run away and shows the design again:

While a run is on, editing is locked (undo, redo and all edits are ignored), so the run and the design never get out of step.

Runners and threads

Two words explain almost everything:

Because all of this state lives in the thread and the runner, never in the wagons themselves, the same train can run on five bricks at once without the bricks interfering with each other.

Character “Brick” trains: ▶ start → … bump wall → … placements: (4, 5) and (12, 5) ▶ run Runner 1 · brick at (4, 5) own hearts, size, pen… order 0 Runner 2 · brick at (12, 5) own hearts, size, pen… order 1 thread: start train bump: not fired yet thread: start train thread: bump train

The 60 Hz clock

The engine simulates in fixed steps of 1/60 of a second. Each screen frame, the real time that passed is added to an accumulator, and as many whole steps as fit are run. A run therefore behaves the same on a fast Mac and a slow iPad. If the device stalls, at most 5 steps are caught up per frame and the rest of the lost time is dropped, so the program slows down rather than jumping ahead.

One step does this, always in the same order:

  1. Fire timers. Every few seconds trains whose time has come are started (if they are not still running).
  2. Run threads in a stable order. Threads are sorted by runner (placements in character order first, then clones in the order they were made), then by the train’s position in the character’s list. Each thread runs until it reaches a wagon that takes time, or until it finishes.
  3. Publish activity. The set of running trains and wagons per runner is handed to the editor, which lights them up.

After the step, the stage lets SpriteKit simulate physics, then checks which characters started touching each other or a wall, and turns those into bump events. Trains started by events, messages and clones begin on the next step.

Screen frameelapsed time dt Engine steps0–5 × 1/60 stimers → threads→ light up wagons PhysicsSpriteKit Contacts,pen, bubbles events start trains on the next step

Instant wagons and wagons that take time

Wagons come in two kinds:

KindWagonsBehaviour
takes timeMove, Turn, Say, Size, Fade, Wait, Wait untilThe thread parks on the wagon. The animation advances a little on each step, and the thread moves on in the step where it completes. Wait until checks its condition once per step.
instantAll othersDone immediately. The thread carries on to the next wagon within the same step.

So a train of Set A = 0 · Change A + 1 · Show A · Move 1 does the first three in the same 1/60 s and then spends a quarter of a second on the move.

Loops can’t freeze the app

Two safety rules keep runaway programs harmless:

Events in detail

No double starts

If an event fires for a runner while that very train is already running on it, the event is ignored. Tapping a character five times while its When tapped train is still turning starts nothing new. Every train that listens to the event starts, not only the first one.

Messages restart

The one exception is When a message arrives. A message restarts the train from its first wagon if it is already running, as Scratch broadcasts do. A speech bubble the old run was showing is removed. This is what makes message ping-pong between characters work (see the Ping-pong sample).

Broadcast events

Buttons, tilts and messages go to every runner, in runner order. Taps go only to the tapped character. Bumps go to both characters involved: if the cat bumps the banana, the cat’s When bumping Banana and the banana’s When bumping Cat both fire.

Contacts

Touching is measured geometrically from each character’s shape, independently of the physics engine. That is why ghosts, which pass through others, still detect touches. A contact begins when the shapes come within 1 pixel of each other and ends once they are more than 2.5 pixels apart. The gap between the two numbers stops a character resting on the floor from bumping it again and again.

Values at run time

Clones and removal

Winning and losing

The moment a Win or Lose wagon runs, the engine records the outcome and stops every thread. No further wagons run, even later ones in the same step. Physics freezes, the celebration plays, and the outcome card appears. Choosing Play again resets and runs again; Edit goes back to the design.

Physics

Physics uses SpriteKit, tuned for the 256-pixel world. The numbers below are what the app uses.

QuantityValue
Gravity (when on)About 400 px/s² downward (25 tiles/s²). Falling across the stage takes about a second.
Weightslight 0.5 · normal 1 · heavy 2.5
KickAn impulse giving a normal-weight body 50 px/s per point of strength. Strength 5 ≈ 250 px/s ≈ a 5-tile jump with gravity on.
MagnetA radial field reaching 128 px (8 tiles), about 200 px/s² at 4 tiles, weakening with distance. It never pulls its owner.
Bouncinessrestitution 0.45, friction 0.3
Air drag0.2 for physics bodies, 0.6 for ghosts (they drift to a stop)
RotationBodies don’t spin from collisions; only hinge joints let them rotate freely.
Player dragPulled at up to 900 px/s; released at up to 500 px/s.
WallsStatic slabs 64 px thick just outside each edge, so fast bodies cannot tunnel through.

Body types

BodyShapeMoves?GravityCollides withDetects touches
Physicscircle (80 % of the sprite)yesyescharacters and wallsyes
Fixedsquare (80 % of the sprite)only by wagons or draggingnois collided withyes
Ghostcircleyesnowalls onlyyes

Move and Go to place the character directly, frame by frame, and clear its speed. This is the same behaviour as the original app: a moving character stays on course, and other physics bodies still bump into it.

Sound during a run

The GAME music plays while the program runs. The two GAME tracks alternate, and the music crossfades from and back to the coding music. Effects are synthesised on the fly as square, triangle and noise chiptune blips: run and stop, bumps (at most one every 0.08 s), messages, the puff of a removal, hearts, and the win and lose jingles. The music ducks briefly under each effect.