Commit Graph

138 Commits

Author SHA1 Message Date
Georg b180637ea7 Hold 60fps: throttle shadow/reflection passes when book geometry is static
The table reflection (full scene re-render) and the book shadow maps each cost ~11ms/frame
and were refreshing at 30Hz even when nothing moved, so every idle/reveal frame paid for one
heavy pass on top of the ~12ms scene render — ~45-52fps.

These passes only need full-rate updates while the book geometry is actually moving (a page
flip). At idle, or during a text reveal where only the page texture mask animates, they now
refresh at 8Hz (candle flicker is the only thing changing them then, captured imperceptibly).
Most non-flip frames are then just the scene render.

pixelRatio is deliberately left at 2x: the book is tilted, so page glyphs are minified along
the tilt and the supersampling is the scene's antialiasing (the composer MSAA is disabled in
app-integration mode). Reducing it blurs text and exposes edge aliasing, so 60fps is bought
from the geometry-independent passes instead. Expressed pixelRatio via devicePixelRatio so it
stays native on HiDPI.

Verified live at WQHD/2x (screenshot-checked crisp text + clean edges): idle ~64fps median
(was 52), reveal ~66fps median (was ~33). Remaining single-digit dips are main-thread page
rasterization during background prepare — addressed by the worker migration.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 09:39:29 +02:00
Georg 1e8defbb55 Spanning playback: one background-prepared path, no fallbacks
For a block that overflows onto the next spread, the plan is now prepared spanning-aware
during the background lookahead — the start spread's reveal timing is derived across both
preview spreads, and the continuation spread's plan is prepared and cached at the same time.

playback then follows a single path:
- activate reuses the prepared start plan (removed the synchronous forceRebuild rebuild).
- revealContinuationSpread reuses the prepared continuation plan (removed the redraw
  fallback); a missing plan is surfaced as a problem, not silently redrawn.

This removes the parallel/immediate prepare distinction and the two fallbacks, leaving one
intended path, and moves the spanning draw work off the critical path.

Verified live on a real spanning block: right line reveals at its area share (~3.3s), the
flip fires, and the continuation appears ~0.3s after the flip (was ~2.7s) and animates
progressively across the next spread over the full TTS — no pop-in, no fast-forward, no
timeline-reveal-continuation-missing. Static suite passes (165).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 09:24:13 +02:00
Georg 28d5e51c92 Reuse spanning-aware prepared plan at activate (kill pre-playback pause)
A spanning block stalled ~2.1s before it began playing: activate ran prepareRevealBlock
with forceRebuild, synchronously redrawing the start spread (and preloading the
continuation spread) on the main thread, because the lookahead plan had been built with
right-only timing before pagination committed the overflow.

Build the start-spread plan spanning-aware during lookahead instead: when the preview
layout shows the block overflows, derive its timing across both preview spreads (via the
revealSpreadSourceOverride) and cache it. activate then reuses that plan — the same fast
cached-plan path non-spanning blocks already use — with no synchronous redraw. forceRebuild
is kept only as a fallback when a block spans but was not prepared spanning-aware (e.g. an
immediate prepare with no preview layout), and an evicted plan still rebuilds correctly
because pagination is committed by then.

Verified live: the spanning block's pre-playback gap dropped from ~2088ms to 139ms (equal
to non-spanning blocks), while the right line still reveals over its area share (~3.3s),
the continuation still animates from the start, and there are no fast-forwards or problems.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 00:10:25 +02:00
Georg 47af10d60c Prepare spanning-block continuation spread in background (kill post-flip redraw)
For a paragraph that overflows onto the next spread, the continuation page was redrawn
synchronously after the flip (drawSpread on the main thread), so the next page stayed
blank for ~2.7s and then the carried-over lines popped in already ~24% revealed instead
of animating from the start.

Move that work off the critical path: during lookahead, prepare and cache the
continuation spread's reveal plan using the not-yet-committed preview spreads (so per-line
timing is computed across both spreads), then reuse it after the flip instead of redrawing.

- pagination: expose the preview spread layout on the returned preview spread so the owner
  can detect the continuation spread (race-free; each call owns its preparedSpreads).
- renderer: revealSpreadSourceOverride lets region collection use preview spreads during
  lookahead; prepareContinuationRevealPlan draws+caches the continuation plan (publishEvent
  off); takeContinuationRevealPlan reuses it, re-stamped as an activate-phase publish.
- timeline: prepare the continuation plan during background (non-immediate) prepares;
  revealContinuationSpread reuses it, falling back to the redraw when none was prepared.

Verified live on a spanning block: continuation now appears ~0.25s after the flip (was
~2.7s) at ve~3471 = the right line's duration, i.e. it animates from the start (no pop-in),
runs to ~full over the TTS, no fast-forward, no continuation-missing problems.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 00:04:21 +02:00
Georg e72594b3ff Revert per-line reveal timing to area-weighting
The previous commit changed per-line reveal-duration distribution from ink-area to
word-count. That dropped a deliberate precision decision (area gives sub-line
granularity) and, verified live on a spanning paragraph, it was what made the
continuation page fail to animate. Restore area-weighting for the per-line split.

The word-share scaling of the *total* duration for partial (spanning) blocks and the
timeline-module timing snapshot/restore are kept — they only preserve existing
word-timings, they do not change the area-based per-line distribution.

Verified: on a real spanning block the right line reveals over its area share (~3.3s),
the page flips, and the continuation animates progressively across the next spread
over the full TTS (no fast-forward, no reveal-all-at-once).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 23:45:26 +02:00
Georg dc2afcf831 Fix spanning-paragraph reveal pacing (right page no longer consumes full TTS)
A paragraph that overflows the right page onto the next spread revealed its single
right-page line over the entire TTS, then timed out (timeline-reveal-commit-timeout)
and only flipped after the whole narration. Two root causes:

- At activate the reused lookahead segment played a sentence instance whose animation
  word-timings were lost (wordTimings=[], totalDuration=0), so reveal timing fell back
  to an area estimate spanning the full TTS. Snapshot the timings at prepare and restore
  them at activate.
- Reveal duration was distributed by ink area, but just-paginated continuation lines
  have ~0 area, so the one right-page line received the whole duration. Distribute by
  word count (reliable) with area as fallback.

Now the right page reveals only its word share (~2.7s for a 6/55-word line), commits,
and flips while TTS continues; the continuation animates on the next spread. Also
rewrote the right-reveal wait to a single timer + commit/fast-forward listeners with
cleanup, removing the stray timeline-reveal-commit-timeout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 17:13:39 +02:00
Georg 6bd1f45362 Reset per-game reveal state on new game so reveal animates over cached content
Starting a new game reuses block ids (1,2,3...). The reveal clock's per-block
start times (activeRevealBlockStarts in the lab) and the renderer's animation/
revealed sets are keyed by block id and were never cleared on a client reset, so
a new game over already-cached content inherited the previous run's start times.
beginPageReveal then computed a huge elapsed and the shader treated the reveal as
already complete — showing everything at once instead of animating.

resetClientPlaybackAndDisplay (run on new game and restore) now emits
story:client-reset; the lab clears activeRevealBlockStarts/pending reveal state,
the texture renderer clears active animations and revealed-block ids, and the
timeline invalidates prepared segments. So each game starts with a clean reveal
clock.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 16:40:31 +02:00
Georg a845108c43 Make WebGL book navigation spread-based and clear stale flip reveal mask
- Navigation now operates on spread indices, not the non-contiguous page-position
  scheme that mapped a forward step onto the same spread (so forward stalled and
  triggered a no-op multi-flip). Forward/back move one spread; start/end and the
  slider use spread indices. The page readout shows the odd page of the visible
  pair (2*spread+1) or 0 at the title spread.
- Flipping forward could show the source page with its last word still masked: a
  stale reveal mask left on the flip surface by a previous playback flip was not
  cleared when the (finished) source page had no active reveal. Reset the flip
  surface reveal shader in that case so the full page shows during the turn.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 16:27:18 +02:00
Georg ab194062bb Fix WebGL reveal pacing on spanning pages and page-reveal-on-flip
- Reveal timing is now word-proportional per page: when a block's reveal only
  covers part of the block (the continuation spread is not paginated at reveal
  time), the page reveals only its share of the TTS, offset by the words before
  it. The right page no longer absorbs the whole TTS before flipping; it flips at
  normal pace and the continuation resumes on the next spread while TTS plays. No
  effect when the regions already cover the whole block (unified plan / one page).
- Page flip start now shows the target spread's same-side page beneath the lifting
  page (revealed as it turns away) instead of a blank that pops in after the flip.
  Deferred (pending-reveal) sides stay blank so the masked reveal still lands via
  activate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 20:01:41 +02:00
Georg 8bb18fa201 Rework WebGL book playback to single ownership and fix flip/reveal pipeline
Establish book-playback-timeline as the sole playback owner driving the
scene through formal webgl-book:* events (not the BookLabDebug surface),
with a single reveal clock in the scene render loop and webgl-page-cache as
the only texture cache. Remove the legacy dual playback path and the
ownsPageFlipCommit gating.

Fixes:
- Flip page detached/folded at the spine: restore the raw page-cap line for
  flip geometry (matches the prototype/pre-regression), removing
  normalizeFlipLineToVisiblePage which moved the pivot off the spine arc.
- Flip textures: distance-based UVs (no horizontal compression),
  direction-aware face material (source on the camera-facing side), source
  meta derived from the visible spread (manual flips), prewarm shape fix.
- Reveal: flash removed on the static page and the flip back surface;
  spanning blocks rebuild the reveal plan at activate and continue the
  reveal on the next spread after the fill flip.
- Cache staleness is contentVersion-primary; nav clamps to spreadCount.

Docs updated to describe the intended single-owner architecture. Regression
checks updated to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 15:30:12 +02:00
Georg c19ebe3089 Stabilize WebGL title and timeline texture flow 2026-06-17 08:31:46 +02:00
Georg ef358c5cfd Stabilize WebGL flip reveal handoff 2026-06-10 15:10:57 +02:00
Georg 97eab216b7 Fix WebGL reveal timing and flip texture readiness 2026-06-10 13:54:54 +02:00
Georg e3d66686b9 Fix WebGL page readiness gating 2026-06-10 10:46:01 +02:00
Georg 623b42caf9 Fix WebGL timeline startup ordering 2026-06-10 10:04:06 +02:00
Georg ce8147b5b1 Enforce explicit WebGL book playback timeline 2026-06-10 09:35:00 +02:00
Georg 5a84923884 Restore WebGL reveal timing diagnostics 2026-06-10 08:09:02 +02:00
Georg 10bf23b10b Add timeline owner for WebGL book playback 2026-06-10 02:00:57 +02:00
Georg b41340151d Checkpoint WebGL book playback refactor state 2026-06-10 01:07:22 +02:00
Georg 171cafeb65 Stabilize WebGL book pagination restore 2026-06-09 16:42:12 +02:00
Georg fe51410a3b Fix WebGL reveal timing and flip prewarm 2026-06-09 10:05:23 +02:00
Georg d665a0f237 Fix WebGL line reveal renderer 2026-06-09 09:02:54 +02:00
Georg 419691000c Fix WebGL page cache and flip sequencing 2026-06-08 23:08:13 +02:00
Georg a73dc5725f Add WebGL page cache and runtime checks 2026-06-08 14:39:42 +02:00
Georg 119cefd4bd Fix WebGL page number texture crash 2026-06-08 10:34:20 +02:00
Georg efd1e6cfff Implement WebGL page reserve navigation 2026-06-08 10:25:54 +02:00
Georg 86b6fa0419 Implement WebGL book spread flip groundwork 2026-06-08 09:13:37 +02:00
Georg c86a304364 Checkpoint WebGL book reveal optimization 2026-06-08 08:19:20 +02:00
Georg 7abd3387f3 Correct WebGL dropcap texture layout 2026-06-07 17:59:01 +02:00
Georg da37608197 Reduce WebGL page texture runtime stalls 2026-06-07 17:37:31 +02:00
Georg 53c24e4fae Stabilize WebGL reveal timing 2026-06-07 16:42:09 +02:00
Georg 74ddd1de1c Gate WebGL book texture fonts 2026-06-07 14:35:00 +02:00
Georg 9434950826 Queue WebGL book reveal masks 2026-06-07 13:52:07 +02:00
Georg 7fc083fb58 Add shader page reveal checkpoint 2026-06-07 13:10:17 +02:00
Georg 7725ce9c73 Soften WebGL paper rendering 2026-06-07 12:22:26 +02:00
Georg de81a7c5c5 Stage WebGL scene loading 2026-06-07 12:08:13 +02:00
Georg 1b593c8c7b Restore WebGL book quality settings 2026-06-07 11:13:05 +02:00
Georg 777e39a650 Correct WebGL book page projection 2026-06-07 09:56:56 +02:00
Georg 081cfa9902 Optimize WebGL book texture reveal 2026-06-06 16:44:15 +02:00
Georg 1b8c8f8bce Add texture drop cap pagination 2026-06-06 15:39:53 +02:00
Georg 431e305df9 Add WebGL FPS cap and texture word reveal 2026-06-06 15:37:44 +02:00
Georg bc736513d4 Restore WebGL control overlay and page grid 2026-06-06 15:17:50 +02:00
Georg 9836c68ffa Add texture-space book pagination foundation 2026-06-06 14:58:25 +02:00
Georg 62215b280f Start texture-space book renderer 2026-06-06 14:51:07 +02:00
Georg b734d83227 Checkpoint WebGL book renderer work 2026-06-06 14:35:37 +02:00
Georg 83ca095d54 Document WebGL page texture pipeline 2026-06-06 11:24:50 +02:00
Georg 0cb1e7c6f5 Fine tune WebGL book indirect lighting 2026-06-06 10:55:49 +02:00
Georg 965be72ea4 Tune WebGL book bounce lighting 2026-06-06 10:41:00 +02:00
Georg 0956d2ef1f Add WebGL book headbands and bounce lighting 2026-06-06 10:29:18 +02:00
Georg 925caa57bb Refine WebGL paper and spine materials 2026-06-06 08:53:29 +02:00