Trajectory theatre
One playhead, four views
Scrub the axis, press play, click a reward column or pick a frame — the inspector, the filmstrip and the reward split all follow the same step. This is the arrangement the workspace should ship; it is also why every component below takes index and onIndexChange rather than owning its own selection.
Filmstrip
Step
- x
- 1
- y
- 1
- goal_dx
- 6
- goal_dy
- 5
- dist
- 7.810
- proximity
- +0.122
- step_cost
- -0.010
- visited
- 0
- clipped
- true
Reward split
ppo-clip@v5 · clipped shaping · reaches the goalStep scrubber
The axis
Reward drawn from a zero baseline, so a penalty reads as a penalty. Event ticks sit above the bars — goal, violation, timeout, a human taking over — because the step that decided the episode should never need to be hunted for in an info dict. Drag it, or use the arrow keys.
workspace/trajectory/step-scrubber.tsxUnbounded proximity: a wall of reward, no goal ticktrajectory-types.tsStep inspector
What changed
Observation, action and info for the scrubbed step — with every field that moved since the previous step tinted and carrying its delta. Reading a trajectory is mostly noticing change, and an undifferentiated wall of numbers hides exactly that.
Step
- x
- 5 +1.000
- y
- 5
- goal_dx
- 2 -1.000
- goal_dy
- 1
- dist
- 2.236 -0.926
- proximity
- +0.190
- step_cost
- -0.010
- visited
- 12 +1.000
- clipped
- true
Scrub the axis above to change thisStep
- x
- 7 -1.000
- y
- 6
- goal_dx
- 0 +1.000
- goal_dy
- 0
- dist
- 0 -1.000
- proximity
- +0.190
- step_cost
- -0.010
- goal_bonus
- +5.000
- visited
- 22 +1.000
- clipped
- true
done=true, goal event, goal_bonus termReward decomposition
Who paid for the return
A scalar reward hides its own exploit. Split the sum and the two runs stop looking like 8.9 versus 41.2 and start looking like what they are: one policy earning a goal bonus, and one policy farming an unbounded proximity term for sixty steps. Click a term to isolate it.
Reward split
Reward split
Diff & filmstrip
Two runs, and the frames that matter
Identical prefixes tell you nothing, so the diff marks the first step where the two policies chose differently and tints everything after it. The filmstrip samples on a stride but always keeps event frames — a fixed interval is exactly what would drop the frame where the run went wrong.
Trajectory diff
Locked playheads; toggle to scrub independentlyFilmstrip
Real frames — the gridworld rendered per step