Iacon Autonomics

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.

Linked — successful run
gridworld · 9×9 · goal (7,6)Seed 17SuccessReturn 8.93
Step 0 / 22Reward 0.113resetReset← → to step

Filmstrip

4 frames · every 8 steps, plus events

Step

0ResetReward0.113
Observation
x
1
y
1
goal_dx
6
goal_dy
5
dist
7.810
Action
reset
proximity
+0.122
step_cost
-0.010
Info
visited
0
clipped
true

Reward split

Return 8.93proximity 44%step_cost 2%goal_bonus 53%Step 0 · proximity +0.12 step_cost -0.01
ppo-clip@v5 · clipped shaping · reaches the goal

Step 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.

Scrubber
gridworld · 9×9 · goal (7,6)Seed 17SuccessReturn 8.93
Step 12 / 22Reward 0.180move east← → to step
workspace/trajectory/step-scrubber.tsx
Scrubber — reward-hacking run
gridworld · 9×9 · goal (7,6)Seed 17TimeoutReturn 41.17
Step 12 / 59Reward 0.549move north← → to step
Unbounded proximity: a wall of reward, no goal tick
Event vocabulary
ResetGoalViolationTimeoutHuman
trajectory-types.ts

Step 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.

Inspector

Step

12Reward0.180
Observation
x
5
+1.000
y
5
goal_dx
2
-1.000
goal_dy
1
dist
2.236
-0.926
Action
move east
proximity
+0.190
step_cost
-0.010
Info
visited
12
+1.000
clipped
true
Scrub the axis above to change this
Inspector — terminal step

Step

22GoalTerminalReward5.180+5.000
Observation
x
7
-1.000
y
6
goal_dx
0
+1.000
goal_dy
0
dist
0
-1.000
Action
move west
proximity
+0.190
step_cost
-0.010
goal_bonus
+5.000
Info
visited
22
+1.000
clipped
true
done=true, goal event, goal_bonus term

Reward 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.

Clipped — goal bonus dominates

Reward split

Return 8.93proximity 44%step_cost 2%goal_bonus 53%Step 12 · proximity +0.19 step_cost -0.01
Unbounded — proximity is the whole return

Reward split

Return 41.17proximity 100%

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

Trajectory diff

Diverges at step 5
ppo-clip@v5 · clipped shaping23 steps8.93
ppo@v4 · unbounded proximity60 steps41.17
ppo-clip@v5 · clipped shapingEpisode endedReward —
ppo@v4 · unbounded proximitymove westReward 0.604
Locked playheads; toggle to scrub independently
Filmstrip

Filmstrip

9 frames · every 8 steps, plus events
Real frames — the gridworld rendered per step