DOM ENGINEV0.21.3FIZLET

0.19.0 RELEASE NOTES

Fizlet 0.19.0

By Chris Cooley ↗ · · CHANGELOG.md ↗

  • Contest: weekly, and small wins. One winner a week, picked from that week's dailies (the gallery's new pieces dated inside the ISO week, at most one a day); no daily winner and no 12-entry minimum. Data moves from site/contest/days/<date>.json to site/contest/weeks/<YYYY-Www>.json, and entries no longer carry a contest stamp (the build fails on one). The prompt is no longer the judge (the 60-character rule is gone); size is: a piece whose live chunk is 10 KB gzip or less (10,240 B, the hero live-chunk budget, measured at build) can win; a bigger one is posted with a "Too big to win" sticker (on /contest/ and its gallery card) and the build fails if it is a week's winner. /contest/ lists the week's dailies smallest first with their measured size and marks the smallest that can win. Archived entries never enter.
  • Gallery: the owner approves every new piece. An entry is live once it carries "approved": "<date>"; a new one lands pending (no approved) and is checked but never built, listed, indexed or entered in the contest until the owner approves it (or pulls it to the archive). The 25 entries already live are grandfathered with their own date. Tests: the gate in tests/gallery.test.mjs, and no card, index row or bundle for a pending entry in tests/site.mjs.
  • Gallery archive: noindex, nofollow (owner: "archive the rest somewhere… but definitely no-follow those pages"). gallery/archive/ is noindex,nofollow, stays out of the sitemap and llms.txt, and the gallery's one quiet link to it is rel="nofollow".
  • Gallery: the "how it works" labs, #42–#46. DOM VIRUS's five Under the hood labs move onto Fizlet as scenes in site/labs/ (one source: the hub vendors them): pool ("One loop. Bounded work.", a real engine.pool of 32), Recall ("Recall gives the page back.", the real Ledger: each letter a Text node, the game's hits in a ledger whose restore() is the RECALL key), duel ("Two Macs, one network."), board ("One host. Two worlds.") and pipeline ("Build once. Ship each."). Each is one engine on the shared clock: read caches layout, step moves state, render writes through the ledger, seeded random; it settles when idle, sleeps off screen, and destroy() gives the figure back exactly. Attract mode plays for 12 s after a visit or the last input. Reduced motion: nothing plays on its own, the keys still work. tests/labs.mjs (Chromium).
  • The fizz: the original can, without the zoom (owner: "the og ones were good, just the zoom needs to be killed"). The 20 × 40 can and its story are back (the 0.18.0 hi-def redraw stays in history, to try other cans later; it never shipped in a release), and fizlet/camera is out: the card holds still, the side and top views tell it. Live chunk 8,752 B gzip (9,455 B for the redraw, 9,710 B with the camera), poster 3,457 B; settled 0 frames a second, measured in Chromium 153.
  • Gallery: quality over quantity. Of the 23 recipe entries made by fe anim, the owner kept 7 (pink sparks, neon rain, POW BAM ZAP, speed lines, halftone wave, red cape, hero landing); the other 16 move into an archive, gallery/archive/ (noindex, out of the gallery's index and search), kept to revisit and numbered A006–A025. The Christmas and Thanksgiving themes live only there. The gallery keeps the fizz, layers and how-it-works.
  • fizlet/timeline (new brick, 2,050 B gzip, budget 2,304; the target was 1,536): scripted DOM choreography on the kernel clock, for the animations still on WAAPI or CSS keyframes (a cartridge flying into a console, a screen growing through clip-path, doors folding open, staggered entrances, looping patrols and diagram loops). timeline(engine, { repeat, yoyo, rate, motion, id }) and .to(targets, values, { dur, delay, ease, stagger, at }): keyframe tracks on CSS values (the numbers change, the words stay: transform, translate, scale, rotate, opacity, clip-path, filter, custom properties) or typed-array slots; easings linear, in, out, inOut, back, steps(n) or a function, each sampled once into a shared 256-step table so the step never calls a function (a call through a function value boxed a number per step in a node test once several easings were in use); sequencing by default, "<", labels, negative delays for overlap; play, reverse, pause, seek, finish, revert, done(); timeline:start, timeline:end and label names on the event ring. Writes happen in render through the ledger, change-only (values rounded to a thousandth); done or paused it never calls busy(), so the engine settles. Reduced motion: play() lands on the end state in one step (motion: true plays a variant built on tl.reduced).
    • Tests: tests/timeline.test.mjs (easings, interpolation, keyframes, sequencing, seek, reverse, finish, repeat and yoyo labels, events and done(), motion: false, settle, revert, Recall, host edits win, setup errors; zero allocation: at most 1 young-generation GC over 200k steps with eight tracks and four easings, and over 200k whole frames once done) and tests/timeline.mjs in Chromium (values mid-way on real elements, 0 frames requested once settled, done or paused, Recall mid-play byte for byte with a host inline style, reduced motion lands on the end).
    • Website: a brick page with a live demo (a cartridge seats in a console; the screen opens; the lines stagger in).
  • Kernel: the fps cap now caps requestAnimationFrame, not just the work (real-browser audit, abe browse). A capped engine used to keep asking for a frame every vsync and skip most of them, so a 30 fps hero still cost 57–120 rAF calls a second (two heroes on the home page: 119.6 rAF/s at 1440). Now, after each frame it leaves the shared clock and one timer brings it back at its next slot: 30 rAF/s for fizz#hero and index#layers, and an uncapped engine on the same page no longer drags a capped one along. Something that puts it back early (a resume, a focus change) gets one frame at once and the timer starts over. Kernel 3,439 B gzip (+17 B); page build 7,127 B (budget 7,168); layers live chunk 9,727 B (its ≤ 9,728 B budget). The fizlet/board-page budget is 4,128 B (was 4,096): it was at 4,095 and esbuild's renamer moves it 2 B with any kernel edit, though it bundles only the kernel's phase constants. Tests: rAF calls per second at 60 and 120 Hz and a capped engine beside an uncapped one (tests/kernel.test.mjs, both fail before), and the fizz story's real page-wide rAF count ≤ 33 a second (tests/site.mjs).
  • Home demo settles. The blast demo ("Blast it. Recall it.") ran its engine every vsync while on screen even when nothing moved (60 rAF/s, blamed on whichever slot was in view: fizz#hero on the home page, gallery#howitworks at 60.4). It now settles half a second after the last letter, wave, particle and scorch mark stop (input wakes it; the HUD reads PAUSED · SETTLED), and its CLICK THE PAGE blink pauses off screen (the gallery slot kept a CSS animation running offscreen at 390). Tests: settles untouched with 0 frames, and no running CSS animation in the gallery slot once scrolled away (tests/site.mjs).
  • Board UI contrast (tokens, both themes). Every key label passes WCAG AA on its face: the shortcut chip (kbd) fades to --bui-kbd-alpha 0.88 instead of 0.7 (rim ink on the command face 3.72 → 4.84:1, on the attack face 4.04 → 5.24:1); the light --k-sys-ink is one shade deeper, #006e91 (4.06 → 5.77:1 on white, 3.58 → 5.08:1 on the page); new derived --k-cmd-deep (command 80% over the rim) under white text: the back tab 3.42 → 4.89:1; the PLAN sticker label is rim ink (4.06 → 5.38:1); a dim aim key's face is aim at 18% (was 28%) so its kbd reads 4.69:1 (was 3.86), and the light --k-fire-ink is #c01033 (its kbd on the light dim face 4.07 → 4.67:1, 5.0:1 on raised). Site: the v0.2 chip sits on --k-cmd-deep (3.42 → 4.89:1) and the light --hl is #0b7a54 (4.36 → 5.35:1). Same hues; tokens.json, board-ui.css and BOARD-UI.md agree. Measured with abe browse's own contrast check: 0 failures in dark (3 before) and light (only the roadmap's large phase numbers remain in light).
  • Tap targets on touch (≥ 44 px, nothing moves). Text links grow their box with padding (taken back with a negative margin where it would take room): footer, card titles, hero, gallery, notes, brick contents and prev/next. The header icon keys are 44 px. Phone screenshots of every changed page are pixel-identical. 668 → 24 small targets over the 30 pages at 390 px; the 24 left are the copy keys, whose hit area grows instead (the kit's invisible ::before) because their face can't.
  • Website: the roadmap tells the truth again. E2 lists rules, steer, coach, camera, perf and arcade as shipped (timeline and layout to come); E3 is nearly done (Null Invaders, Byte Runner, Sherman Bytes and DOM Battlefield at 1.0, Battlefield Blitz on Fizlet, Swarm Protocol 1.4 in review) and adds "every animation on the sites on Fizlet, heroes first"; E4 adds the room server's invite gate and spend caps; E5 adds the gallery and notes.
  • fizlet/arcade: every HUD preset's panel drags (owner: "the in-page scoreboard is always draggable"). The arcade card and the skirmish bar move from anywhere that isn't a key, mouse or touch, and stay on screen when the window shrinks (shared in arcade-parts). Browser test at 2× for both presets: the canvas at CSS size, the drag, the clamp.
  • Fix: games with exactOptionalPropertyTypes compile again. pressCue (board-ui) and coach passed an optional signal straight into listener options, which strict consumers (the arcade games) reject; 0.18.0 broke their typecheck. Same runtime, no byte change. npm run typecheck now also runs with exactOptionalPropertyTypes, so it can't regress.
  • Website: gallery #032, "the transformer fights the diffuser to a KO". Made by fe anim from the prompt (recipe fight, the fight scene, gag ko; kernel + sprite + fx + slot): live 9,490 B · total 11,416 B gzip (with the 1,926 B loader) on this build, loaded only near the viewport, never under reduced motion; settles to zero frames.
  • Website: gallery #033, "the tokenizer counts tokens in a punch combo". Made by fe anim from the prompt (recipe fight, the fight scene, gag combo; kernel + sprite + fx + slot): live 9,493 B · total 11,419 B gzip (with the 1,926 B loader) on this build, loaded only near the viewport, never under reduced motion; settles to zero frames.
  • Website: gallery #034, "the context window takes punches until it overflows". Made by fe anim from the prompt (recipe fight, the fight scene, gag overflow; kernel + sprite + fx + slot): live 9,492 B · total 11,418 B gzip (with the 1,926 B loader) on this build, loaded only near the viewport, never under reduced motion; settles to zero frames.
  • Website: gallery #035, "the hallucinator punches a mirage". Made by fe anim from the prompt (recipe fight, the fight scene, gag hallucinate; kernel + sprite + fx + slot): live 9,491 B · total 11,417 B gzip (with the 1,926 B loader) on this build, loaded only near the viewport, never under reduced motion; settles to zero frames.
  • Website: gallery #036, "the overfitter blocks every jab it trained on". Made by fe anim from the prompt (recipe fight, the fight scene, gag overfit; kernel + sprite + fx + slot): live 9,494 B · total 11,420 B gzip (with the 1,926 B loader) on this build, loaded only near the viewport, never under reduced motion; settles to zero frames.
  • Website: gallery #037, "the diffuser denoises out of static and wins". Made by fe anim from the prompt (recipe fight, the fight scene, gag diffuse; kernel + sprite + fx + slot): live 9,489 B · total 11,415 B gzip (with the 1,926 B loader) on this build, loaded only near the viewport, never under reduced motion; settles to zero frames.
  • Website: gallery #038, "a to the moon rocket launches off a button". Made by fe anim from the prompt (recipe meme, the meme scene, bit rocket; kernel + sprite + fx + slot): live 9,233 B · total 11,159 B gzip (with the 1,926 B loader) on this build, loaded only near the viewport, never under reduced motion; settles to zero frames.
  • Website: gallery #039, "diamond hands sparkle and never let go". Made by fe anim from the prompt (recipe meme, the meme scene, bit diamond; kernel + sprite + fx + slot): live 9,240 B · total 11,166 B gzip (with the 1,926 B loader) on this build, loaded only near the viewport, never under reduced motion; settles to zero frames.
  • Website: gallery #040, "a candle chart pumps then dumps". Made by fe anim from the prompt (recipe meme, the meme scene, bit candles; kernel + sprite + fx + slot): live 9,238 B · total 11,164 B gzip (with the 1,926 B loader) on this build, loaded only near the viewport, never under reduced motion; settles to zero frames.
  • Website: gallery #041, "laser eyes mascot powers on and fires". Made by fe anim from the prompt (recipe meme, the meme scene, bit laser; kernel + sprite + fx + slot): live 9,239 B · total 11,165 B gzip (with the 1,926 B loader) on this build, loaded only near the viewport, never under reduced motion; settles to zero frames.

Every release