feat(sim): Right axis = deterministic real-time painterly dream

Replace the pre-baked SD restyle variants (too fluid — the painting re-rolled
every keyframe and the flow-warp melted frames) with a deterministic, real-time
dream that holds STILL across the loop; only the footage's own motion moves.
Design: docs/superpowers/specs/2026-06-22-right-axis-deterministic-dream-design.md

Engine (player/alteration.py): Restyle(variant) -> Dream(strength, intensity);
Calibration.right_variant_map -> dream_gain. player/state.py: a Right change is a
LIVE_UPDATE now, not a crossfade (only a clip swap crossfades). Plan dict
restyle -> dream. Tests migrated; 233 green. (No new Python behavior for the
client-render swap below.)

Renderer (simulator front-end): the Right dream is a WebGL2 Kuwahara shader on a
<canvas> over the live video — edge-preserving, so motion/structure stay crisp;
the dream is the same footage, just stylized (no blur). Ramped by the knob; max
goes "trippy" (vivid saturation + ~45deg hue drift + posterize, concentrated near
max via t=intensity^2). Phase-1 luminous-haze (CSS+bloom) was built then retired
by eye (blur read as out-of-focus video). Mood grade rides as a CSS filter on the
canvas; bloom layer removed.

Dev ergonomics that fell out of the iteration:
- /dev/version + a 1s client poll = live-reload (open tab never runs stale
  renderer code; reloads on my edits AND server restarts).
- no-cache on the dev server; an on-screen error banner + crash-proof render so a
  silent throw can't masquerade as "nothing is changing".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ben Stull
2026-06-23 09:55:35 -07:00
parent 6adb93407b
commit cbb7c9f53d
10 changed files with 404 additions and 92 deletions
+25 -16
View File
@@ -7,8 +7,8 @@ from player.alteration import (
AnalyticalOverlay,
Calibration,
ColorGrade,
Dream,
RenderPlan,
Restyle,
plan_alteration,
render_plan_to_dict,
)
@@ -23,7 +23,8 @@ def test_all_zero_knobs_is_the_unaltered_base():
assert plan.is_identity
assert plan.overlay.level == 0
assert plan.overlay.intensity == 0.0
assert plan.restyle.variant == 0
assert plan.dream.strength == 0
assert plan.dream.intensity == 0.0
assert plan.grade.tone == 0.0
assert plan.grade.is_identity
@@ -32,21 +33,26 @@ def test_left_drives_the_analytical_overlay_only():
plan = plan_alteration(_coord(left=4))
assert plan.overlay.level == 4
assert plan.overlay.intensity == 1.0
assert plan.restyle.variant == 0 # Left does not touch the substrate
assert plan.dream.strength == 0 # Left does not touch the substrate
assert plan.dream.intensity == 0.0
assert plan.grade.tone == 0.0
def test_right_selects_a_discrete_restyle_variant_only():
def test_right_drives_the_deterministic_dream_only():
# Right-axis dream reframe (session 0013): Right is a live deterministic
# haze, carried as strength (0..4) + intensity (0..1), not a baked variant.
plan = plan_alteration(_coord(right=2))
assert plan.restyle.variant == 2
assert plan.dream.strength == 2
assert plan.dream.intensity == pytest.approx(0.5)
assert plan.overlay.level == 0 # Right does not add overlay
def test_left_and_right_stack_not_cancel():
# design §4.2: whole-brain corner = dreamlike substrate WITH labels on top
# design §4.2: whole-brain corner = dream substrate WITH labels on top
plan = plan_alteration(_coord(left=4, right=4))
assert plan.overlay.level == 4
assert plan.restyle.variant == 4
assert plan.dream.strength == 4
assert plan.dream.intensity == 1.0
def test_affect_needs_both_knobs_up():
@@ -112,7 +118,8 @@ def test_dark_minus_light_sets_intermediate_tone():
def test_whole_brain_dark_corner_stacks_grade_substrate_and_overlay():
plan = plan_alteration(_coord(left=4, right=2, dark=4, light=0))
assert plan.overlay.level == 4
assert plan.restyle.variant == 2
assert plan.dream.strength == 2
assert plan.dream.intensity == pytest.approx(0.5)
assert plan.grade.tone == -1.0
assert not plan.is_identity
@@ -124,32 +131,34 @@ def test_default_calibration_is_locked():
# locked feel is a deliberate edit here + in alteration.py.
assert DEFAULT_CALIBRATION.mood_gain == 1.0
assert DEFAULT_CALIBRATION.overlay_gain == 1.0
assert DEFAULT_CALIBRATION.right_variant_map == (0, 1, 2, 3, 4)
assert DEFAULT_CALIBRATION.dream_gain == 1.0
def test_default_calibration_is_behavior_preserving():
# DEFAULT_CALIBRATION must reproduce the original three helpers exactly.
# DEFAULT_CALIBRATION reproduces the per-axis helpers exactly.
for left in range(5):
assert plan_alteration(_coord(left=left)).overlay.intensity == pytest.approx(left / 4)
for right in range(5):
assert plan_alteration(_coord(right=right)).restyle.variant == right
assert plan_alteration(_coord(right=right)).dream.strength == right
assert plan_alteration(_coord(right=right)).dream.intensity == pytest.approx(right / 4)
for dark in range(5):
for light in range(5):
expected = (light - dark) / 4
assert plan_alteration(_coord(dark=dark, light=light)).grade.tone == pytest.approx(expected)
def test_custom_calibration_scales_mood_and_overlay():
cal = Calibration(mood_gain=0.5, overlay_gain=0.5, right_variant_map=(0, 0, 1, 1, 2))
def test_custom_calibration_scales_mood_overlay_and_dream():
cal = Calibration(mood_gain=0.5, overlay_gain=0.5, dream_gain=0.5)
assert plan_alteration(_coord(light=4), cal).grade.tone == pytest.approx(0.5)
assert plan_alteration(_coord(left=4), cal).overlay.intensity == pytest.approx(0.5)
assert plan_alteration(_coord(right=3), cal).restyle.variant == 1
assert plan_alteration(_coord(right=4), cal).dream.intensity == pytest.approx(0.5)
def test_calibration_gain_is_clamped_to_unit_range():
cal = Calibration(mood_gain=10.0, overlay_gain=10.0)
cal = Calibration(mood_gain=10.0, overlay_gain=10.0, dream_gain=10.0)
assert plan_alteration(_coord(light=4), cal).grade.tone == 1.0 # clamped, not 10
assert plan_alteration(_coord(left=4), cal).overlay.intensity == 1.0
assert plan_alteration(_coord(right=4), cal).dream.intensity == 1.0
def test_render_plan_to_dict_round_trips_the_numbers():
@@ -158,7 +167,7 @@ def test_render_plan_to_dict_round_trips_the_numbers():
"grade": {"tone": -1.0},
"overlay": {"level": 4, "intensity": 1.0},
"affect": {"strength": 2, "intensity": 0.5},
"restyle": {"variant": 2},
"dream": {"strength": 2, "intensity": 0.5},
"is_identity": False,
}
+6 -4
View File
@@ -59,13 +59,15 @@ def test_overlay_change_is_a_live_update():
assert t.playback.plan.overlay.level == 4
def test_restyle_change_crossfades_the_substrate():
# design §4.3: the Right v2v substrate is a pre-baked variant swap
def test_dream_change_is_a_live_update_not_a_crossfade():
# Right-axis dream reframe (session 0013): the Right dream is a deterministic
# LIVE filter now, so changing it no longer crossfades — only a clip swap does.
p = Player(LIB)
p.update(_controls(content="video", right=0))
t = p.update(_controls(content="video", right=4))
assert t.kind == TransitionKind.CROSSFADE
assert t.playback.plan.restyle.variant == 4
assert t.kind == TransitionKind.LIVE_UPDATE
assert t.playback.plan.dream.strength == 4
assert t.playback.plan.dream.intensity == 1.0
def test_volume_only_change_is_a_live_update():
+3 -2
View File
@@ -38,7 +38,8 @@ def test_alteration_returns_the_engine_plan(client):
assert resp.status_code == 200
data = resp.json()
assert data["plan"]["overlay"]["level"] == 4
assert data["plan"]["restyle"]["variant"] == 2
assert data["plan"]["dream"]["strength"] == 2
assert data["plan"]["dream"]["intensity"] == 0.5
assert data["plan"]["grade"]["tone"] == -1.0
assert data["content"]["video"] is True
@@ -51,7 +52,7 @@ def test_alteration_honors_off_as_black(client):
def test_alteration_accepts_calibration(client):
body = {"controls": _controls(light=4),
"calibration": {"mood_gain": 0.5, "overlay_gain": 1.0, "right_variant_map": [0, 1, 2, 3, 4]}}
"calibration": {"mood_gain": 0.5, "overlay_gain": 1.0, "dream_gain": 1.0}}
resp = client.post("/api/alteration", json=body)
assert resp.json()["plan"]["grade"]["tone"] == 0.5