# REPORT — 13-pixel-art / 「像素冒险 PIXEL QUEST」

## Concept (two sentences)
A 10-second retro side-scroller **attract sequence**: an 8-bit adventurer sprints and leaps through
a parallax world as the palette slides from morning cyan to a golden sunset, bopping a slime,
typing a greeting, and cracking open a treasure chest whose light burst is the film's hero moment.
The chunky **PIXEL QUEST** logo then slams down onto a CRT title card with a shine sweep, a Chinese
subtitle and a blinking `PRESS START`.

Everything is drawn on a 320×180 canvas with **only 16 colours** and blitted ×6 with
nearest-neighbour, then passed through a CRT pass (scanlines, phosphor bloom, barrel, tube mask).

---

## Beat sheet (timecodes)

| time | beat | picture | sound |
|---|---|---|---|
| 0.00 | **HOOK — run-in burst** | scene already alive (no black); hero rockets in from off-screen left with speed lines + dust | kick + lead stab + noise hit + whoosh |
| 0.92 / 1.11 / 1.31 | coin run ×3 | three coins in an arc; each pops into a sparkle burst on contact, +1 HUD | ascending two-note coin blips, panned L→R |
| 1.50–1.60 | anticipation | hero crouches (3-frame hold) before the pit | — |
| 1.60–2.15 | **jump the pit** | pit scrolls under; 3-phase jump: crouch → launch → arc → 4-frame squash landing + dust | rise chirp @1.60, kick+noise thud @2.15 |
| 2.50–2.58 | second anticipation | crouch again | — |
| 2.58–2.78 | arc onto the slime | slime hops in place, hero arcs over | hop chirp |
| 2.78–3.05 | **bop the slime** | hero lands on it → slime squashes flat and bursts into green pixels; 2-px screen quake; hero bounces | impact + boing + noise @2.78 |
| 2.99 / 3.22 / 3.41 | coins ×3 | second coin arc, collected mid-run | coin blips |
| 3.4–4.6 | calm / sunset deepens | camera decelerates to a stop, birds cross the sky, two gems collected | gem bells @3.92 / 4.34, music drops to pads |
| 4.05–5.30 | **dialogue** | pixel RPG box slides up with a 「勇者」 name tab; 「欢迎来到像素世界！」 types in char-by-char; ▼ blinks | box open blip, 9 per-character blips, close blip |
| 5.45 | chest reveal | chest sits on a pedestal under a dithered shaft of light | bell shimmer + whoosh |
| 5.85 | chest opens | lid pops back in stepped frames | click + pop |
| **5.95** | **HERO MOMENT — LIGHT BURST** | radial white blow-out (keeps the scene readable), 16 pixel rays, expanding shockwave ring, 5-px screen quake, and coins/gems/sparks fountaining on real ballistic arcs | riser 5.40→5.95 into a big impact + two bells |
| 6.05–6.35 | celebration hop | hero does a small joy hop; burst keeps settling | — |
| 7.35 | **logo drop** | **PIXEL QUEST** drops in letter-by-letter (1.1-frame stagger) from above onto a navy title card that pops in with the impact; each letter overshoots and settles | heavy impact + sub + noise, secondary impact @7.47 |
| 7.90–8.30 | shine sweep | a white specular band crosses the letters L→R | whoosh |
| 8.15–8.50 | subtitle | 「像素冒险」 types in under a gold rule | 7 short blips |
| 8.6–10.0 | **composed end frame** | held title card with living micro-motion: blinking `PRESS START`, ambient twinkles, drifting clouds, idle breathing, sun glow pulse, CRT scanline shimmer | pad + slow bell arpeggio, fade out |

---

## How each signature feature is realised

| signature feature | where / how |
|---|---|
| 大颗粒像素 + 硬边缘 | authored on a **320×180** buffer, blitted to 1920×1080 at exactly **×6** with `imageSmoothingEnabled=false` (same for the barrel pass). No anti-aliasing in the pixel path. |
| 有限色板 (8–32 色) | exactly **16** colours (PICO-8 palette) in `parts/pico.mjs`. Every sprite, layer, flash and beam is dither-picked from those 16. |
| 8–12fps 精灵帧动画 | hero run cycle = **6 frames at 12 fps** (`Math.floor(t*12)%6`), idle = 4 frames at 8 fps, slime = 3-frame hop at 8 fps, coin = 4-frame spin. Camera/parallax move every frame; the sprites step. |
| 抖动渐变 dithering | the sky is a 4×4/8×8 **ordered Bayer** ramp between palette entries, baked into 6 palette steps the film swaps as the sun sets. Beams, flashes, shadow, pit depth and twinkles use the same ordered dither. |
| CRT 扫描线滤镜 | `parts/post.mjs`: 90-strip **barrel** distortion, dark scanline every 3rd output row, quarter-res **phosphor bloom** (additive + vertical smear), warm tube cast, deterministic flicker, vignette and a rounded **tube mask** — tuned so the ×6 grid stays hard. |
| 视差卷轴（远中近三层整像素平移） | **four** bands — mountains ×0.12, forest ×0.34, bushes ×0.60, ground ×1.00 — plus a static dithered sky. Every offset is `Math.round`ed → whole-pixel motion. |
| 阶梯时序 (steps, 非平滑缓动) | all sprite animation is stepped; sky palette shifts in discrete steps; chest lid opens in stepped frames; squash/stretch is integer-scaled. Easing is used only for the camera and the logo's physical drop. |
| sprite sheet + 序列帧循环 | the Aseprite-style sheet, built in code: `parts/sprites.mjs` authors every frame as pixel code into a shelf-packed sprite atlas baked once at load; blits are sub-rect draws. |
| 品牌怀旧 / 8-bit MV feel | the attract-screen structure itself: HUD (hearts, coin counter, progress bar), dialogue box with name tab, title card + `PRESS START`, and a chiptune score with coin/jump/stomp SFX. |

---

## Tech route
`index.html` exposes `window.renderAt(t)` / `window.__ready`. Each call:
1. draws the whole film into a 320×180 offscreen canvas — sky (baked, 6 palette steps), sun, clouds,
   birds, 4 parallax bands (baked, 6 palette steps each), pit, entities, hero, particles, burst,
   logo, dialogue, HUD — then
2. `present()` does the ×6 nearest upscale, barrel strip pass, bloom, scanlines, tint, vignette and
   tube mask onto the 1920×1080 display canvas.

Allocation-heavy work (skies, bands, sprite atlas, logo card, particle tables) is **precomputed once
at load**; a frame only blits, dithers small regions and draws text masks. Measured ~0.26 s/frame
end to end, most of which is the harness's PNG screenshot of 1920×1080 — the page's own draw is
well inside the ~250 ms budget.

### Harness / shared bugs
None found in `harness/` or `shared/` — nothing there needed changing. (The one real bug was mine:
`blitScale` passed destination coordinates into the **source** rectangle of
`drawImage(image, sx,sy,sw,sh, dx,dy,dw,dh)`, so the hero silently drew nothing. Fixed to
`drawImage(canvas, 0,0,w,h, dx,dy,dw,dh)`.)

---

## Check numbers (from `out/13-pixel-art/qa.json`)
| check | value | target |
|---|---|---|
| duration | **10.000 s** | exactly 10.000 s ✔ |
| resolution | **1920×1080** | ✔ |
| fps | **30** (300 frames) | ✔ |
| codec | H.264 `yuv420p` + AAC 48 kHz stereo | ✔ |
| integrated loudness | **−15.0 LUFS** | ≈ −14 (−14±2) ✔ |
| true peak | **−2.7 dBTP** | ≤ −1 dBTP ✔ |
| black frames | **0** | 0 ✔ |
| frozen frames | **0** | — |
| console errors | **0** | 0 ✔ |
| PROBLEMS line | **none** | none ✔ |

---

## Honest top-3 weaknesses
1. **The hero is the weakest link.** At 20×27 he is authentic for the genre but small on a 1080p
   frame, and his run cycle is *parametrised* (limbs solved from a phase function) rather than six
   individually drawn frames — so the arms/legs read as thin sticks with no secondary motion (no
   scarf, no cape, no 2-px hair bob). A hand-drawn sheet with overlap and follow-through would lift
   the whole film.
2. **The first three seconds are the least distinctive composition.** Morning cyan + evenly spaced
   conical trees + flat brown dirt reads "competent platformer", not "award-shortlist". The sunset
   half and the title card are far stronger; the mid-ground forest in particular is repetitive and
   would benefit from depth layers, varied tree shapes and warmer morning light.
3. **Sound and sync are good but the music is thin.** The score is a 4-chord loop with an arpeggio
   rather than a bespoke melody with a memorable hook, and the master lands at −15.0 LUFS (1 dB
   under target) because the kick transients cap what `loudnorm` can add — a limiter on the music
   bus alone would let it sit at −14 with the SFX peaks intact. The hero-moment flash also washes
   the frame for ~2 frames more than intended, and the pit still reads slightly more like a dark
   slab than a deep hole.

## Self-scores (out of 10)
| dimension | score | note |
|---|---|---|
| style fidelity | **9** | every listed signature technique present and executed correctly; an 8-bit expert names it instantly |
| concept | **8** | a canonical, confident attract sequence, but a familiar one — well-executed rather than surprising |
| motion craft | **8** | stepped sprite timing, anticipation→launch→settle on both jumps, 4-frame squash on landing, letter stagger + overshoot on the logo, impact quakes; penalised for the thin run cycle and a couple of long-ish settles |
| design & typography | **8.5** | strong title card, controlled 16-colour hierarchy, clean CJK (「欢迎来到像素世界！」/「像素冒险」 set correctly with proper punctuation), game HUD; the day half is the weaker composition |
| finish & texture | **8.5** | CRT pass, ordered dither, no banding, no clipped elements, no fallback fonts; the pit depth and 2-frame flash wash are the soft spots |
| sound & sync | **8.5** | chiptune with correct instrumentation, every visual hit lands on an audio onset, mastered; music is a loop and integrated is 1 dB low |
| technical | **9** | fully deterministic pure-function-of-t frames, console clean, no black/frozen frames, exact 10.000 s / 1920×1080 / 30 fps, well inside the frame budget |

**Average 8.5.** Weakest dimensions are *concept* and the hero/music sub-craft — what I would fix
first with more time.
