Skip to content
Docs
Open Promoeyes
pp-lab.next

pp-lab.next

Post-processing lab — FIRST-CLASS STEPS. pp-lab.promo's three shots, rebuilt with the engine-ordered post steps (docs/plans/postprocess.plan.md), plus a fourth shot that only the new grammar can author.

Read the rig's notes
The steps, and the order the ENGINE runs them in whatever order they are
written (packages/engine/engine/post/steps.ts):
  bloom   30  strength radius threshold selective …
  output  40  tone_mapping=aces|agx|neutral|reinhard|cineon|linear|none exposure
  aa      50  method=ssaa samples=4 — always, written LAST in the chain
  film    60  grain scanlines line_count vignette vignette_start
Every numeric param is a channel (pug short rows or properties > kf, any
easing); `enabled` is a channel too.

ANTIALIASING · `step(name=aa method=ssaa samples=4)`, the last step.
  Never FXAA (`aa method=fxaa` / `step(name=fxaa)`): house rule.
  samples = full scene renders per frame, a power of two (1 2 4 8 16 32).
  Never 3: the engine rounds log2, so 3 silently costs 4 renders.
  8 only for a shot whose thin near-diagonal lines stair-step at 4 —
  say why in a comment.

DEPTH OF FIELD · on the CAMERA, never as a post step:
  camera(dof_pass=three dof_range=<mm> dof_blur=<px at 1080p> [focus=…])
  dof_range = mm from the focus plane to full blur; dof_blur = disc size.
  focus = mm | "x y z" | node id. With a frame track and no focus on its
  kfs, the lens follows the framing (look_at / node=) — shots 1 and 4.
  kf(… focus= dof_range= dof_blur=) keys the lens per pose; a kf focus
  is a number (mm) only — shot 3.
  A static camera with no frame track needs focus=, else it sits at 10 mm.
  Camera DOF runs at the end of the chain (slot 70, after film).
  Keep hot emissives (the halo rings) OUTSIDE the focus band: they bloom
  sharp, then the lens spreads them into soft bokeh.
  dof_pass=three reads about 1.5× narrower than the default pass at the
  same dof_blur on these dark sets — shots 1 and 4 are tuned by eye.
  A bright UI page that goes out of focus stays on the default pass (no
  dof_pass): three's max filter breaks its dark text into scribbles — shot 3.
  Do not author `step(name=dof …)`: it takes over the lens (camera focus /
  dof_range / dof_blur are then ignored) and it reads method=/pass=, so a
  dof_pass= written on it does nothing.

Shots 1–3 translate pp-lab.promo one to one:
  film_grain(output_transform=1) + fxaa  →  output(aces) + film + aa(ssaa)
  film_grain(output_transform=2)         →  output(none)
  camera focus/dof_range/dof_blur        →  the same; dof_pass=three in shot 1
  4 · LIVE  everything keyed: the lens opens (dof_blur 6 → 33 on the
            camera kfs) as focus follows the push from phone to laptop,
            bloom swells, AgX exposure lifts, and the film layer switches
            off at 3.5 s.

THE SET and THE SCREENS as pp-lab.promo.

Preview: http://localhost:4444/runtime?project=promos/primitives/postprocessing/pp-lab.next.promo&renderer=webgpu
Scenes
4
Kind
reference rig
//
    Post-processing lab — FIRST-CLASS STEPS. pp-lab.promo's three shots,
    rebuilt with the engine-ordered post steps (docs/plans/postprocess.plan.md), plus a
    fourth shot that only the new grammar can author.

    The steps, and the order the ENGINE runs them in whatever order they are
    written (packages/engine/engine/post/steps.ts):
      bloom   30  strength radius threshold selective …
      output  40  tone_mapping=aces|agx|neutral|reinhard|cineon|linear|none exposure
      aa      50  method=ssaa samples=4 — always, written LAST in the chain
      film    60  grain scanlines line_count vignette vignette_start
    Every numeric param is a channel (pug short rows or properties > kf, any
    easing); `enabled` is a channel too.

    ANTIALIASING · `step(name=aa method=ssaa samples=4)`, the last step.
      Never FXAA (`aa method=fxaa` / `step(name=fxaa)`): house rule.
      samples = full scene renders per frame, a power of two (1 2 4 8 16 32).
      Never 3: the engine rounds log2, so 3 silently costs 4 renders.
      8 only for a shot whose thin near-diagonal lines stair-step at 4 —
      say why in a comment.

    DEPTH OF FIELD · on the CAMERA, never as a post step:
      camera(dof_pass=three dof_range=<mm> dof_blur=<px at 1080p> [focus=…])
      dof_range = mm from the focus plane to full blur; dof_blur = disc size.
      focus = mm | "x y z" | node id. With a frame track and no focus on its
      kfs, the lens follows the framing (look_at / node=) — shots 1 and 4.
      kf(… focus= dof_range= dof_blur=) keys the lens per pose; a kf focus
      is a number (mm) only — shot 3.
      A static camera with no frame track needs focus=, else it sits at 10 mm.
      Camera DOF runs at the end of the chain (slot 70, after film).
      Keep hot emissives (the halo rings) OUTSIDE the focus band: they bloom
      sharp, then the lens spreads them into soft bokeh.
      dof_pass=three reads about 1.5× narrower than the default pass at the
      same dof_blur on these dark sets — shots 1 and 4 are tuned by eye.
      A bright UI page that goes out of focus stays on the default pass (no
      dof_pass): three's max filter breaks its dark text into scribbles — shot 3.
      Do not author `step(name=dof …)`: it takes over the lens (camera focus /
      dof_range / dof_blur are then ignored) and it reads method=/pass=, so a
      dof_pass= written on it does nothing.

    Shots 1–3 translate pp-lab.promo one to one:
      film_grain(output_transform=1) + fxaa  →  output(aces) + film + aa(ssaa)
      film_grain(output_transform=2)         →  output(none)
      camera focus/dof_range/dof_blur        →  the same; dof_pass=three in shot 1
      4 · LIVE  everything keyed: the lens opens (dof_blur 6 → 33 on the
                camera kfs) as focus follows the push from phone to laptop,
                bloom swells, AgX exposure lifts, and the film layer switches
                off at 3.5 s.

    THE SET and THE SCREENS as pp-lab.promo.

    Preview: http://localhost:4444/runtime?project=promos/primitives/postprocessing/pp-lab.next.promo&renderer=webgpu
promo#pp-lab-next(width=1920 height=1080 fps=30)
  materials
    // emissive well past the 0.85 threshold: selective bloom only sees emissive light
    standard#glow(color=#1a1206 emissive=#ffb347 emissive-intensity=2.2 metalness=0 roughness=1)
    standard#glow-cyan(color=#06121a emissive=#7ad7ff emissive-intensity=2 metalness=0 roughness=1)
    standard#shell(color=#26262b metalness=0.4 roughness=0.3 envmap-intensity=1.1)
    standard#deck(color=#08090c metalness=0.1 roughness=0.8)
  // ===================================================================
  // SCREENS — html2ui output (pp-lab.screens.html)
  // ===================================================================
  template#scr-ph-cam
    card#ph-cam(x=0 y=0 w=71 h=154 r=0 fill=#07080c shadow=false)
      card(x=0 y=0 w=71 h=154.02 r=0 fill=#07080c clip=true)
        text(x=5.1 y=4.01 size=2.91 weight=700 color=#f4