site/kit/ui/savers
0 directories and 9 files in site/kit/ui/savers.
savers
- The four legacy screensavers, rebuilt as vanilla ESM over one canvas: no React, no build step, no dependency.
saver(canvas, name, opts)mounts one and returnsstop();SAVERSis['matrix', 'sleep', 'mandelbrot', 'julia'].opts.seedmakes a saver deterministic through a tiny xorshift; no seed and it draws fromMath.random.- The seed reaches the choices, never the clock: the fractals pick their target, spin and rotation from
rand, but zoom, fade and colour rideperformance.now(), so two seeded mounts agree on what they draw and not on when. - Every colour is read from the custom properties on the canvas:
--art(with--groundbehind it) is the ground,--accentis the primary ink of every saver. No literal ever, no random hue. --accentis the Tint setting, so switching tint recolours a running saver:chrome.jstint()fires the windowthemeevent andframe.jsre-reads the palette on it.frame.jsowns the life: it sizes the canvas to its CSS box timesmin(devicePixelRatio, 2)under a ResizeObserver, pauses under an IntersectionObserver and ondocument.hidden, re-reads the palette on the windowthemeevent and on aprefers-color-schemechange, and underprefers-reduced-motiondraws one still frame and stops.- A saver mounted offscreen never starts: with an IntersectionObserver present it waits for the first callback, so nothing runs until the canvas is seen, and pausing clears the rAF and the
everyinterval both, never merely skipping draws. - That still is
view.still, handed to the maker so a saver whose motion is a fade can draw its one frame fully lit instead of at age zero. - A canvas keeps the first context it is given, so a caller switching between a 2d saver and a WebGL one must mount on a fresh canvas element.
- A fractal that gets a context but no shader program still hands back a
stop()that callsWEBGL_lose_context, so a failed mount never strands one of the browser's few WebGL contexts. - The caller gates:
saver()returns a no-op stopper when there is no canvas or no such name, and nothing runs without JS, so a page must ship a static fallback (a still image or a plain ground) inside or behind the canvas.
SAVERS
matrix: MrlyFont rain in--accent, one glyph of0-9A-Zper 20px column per 33ms tick, trails erased by the ground at 5% alpha.sleep: the DVD bouncer, one mrlytile ofmin(w, h) * 0.15at 4px a frame over a transparent ground, filled cells in--accent, re-rolling its design, its number and its level on every wall.mandelbrot: the set under{-2, 1, -1.5, 1.5}, zooming a whole cycle into a boundary point the wayfinder found.julia: the same engine over{-1.5, 1.5, -1.5, 1.5}with one of six c presets picked at mount.
CONSTANTS
- Matrix: cell 20, tick 33ms, alphabet
0-9A-Z, reset wheny > hand the roll clears 0.975,--accentfor the whole field. - Sleep: side
0.15of the smaller axis, velocity 4px a frame with random signs, marks are the five mrlytiles over numbers3 5 7 9at level 1 or 2, drawn as snapped rects so it stays crisp. - Mrlytiles:
carpetis on where at most one coordinate is odd,netwhere at least one is,htreeon the even rows,vtreeon the even columns,voidwhere the two share a parity; level 2 is the level-1 grid Kroneckered with itself,n^2a side. - Fractal: cycle 65536ms, fade 2048ms, zoom
pow(128, t), rotation1/2048a frame in a random direction, colour1/256a frame, start scale1/2, escape radius 128,maxIter = 100 + floor(50 * log2(zoom))under a hard cap of 1000. - Every cycle re-rolls the target, the spin direction and the starting rotation;
stop()clears the inline opacity the fade wrote. - Fractal colour: inside is the ground, outside is
mix(ground, accent, 0.5 + 0.5 * cos(3.0 + sl * 0.15 + t))over the smooth countsl, the accent being--accent. - Wayfinder: 200 random points at 150 iterations a cycle, keeping the highest escape count that still escapes, which is the boundary.
- Julia presets:
-0.4+0.6i,-0.8+0.156i,0.285+0.01i,-0.7269+0.1889i,-0.1+0.651i,0.355+0.355i.
MOUNT
- Full screen, click anywhere to wake, the way the legacy did it:
<canvas id="saver" style="position: fixed; inset: 0; width: 100vw; height: 100vh; background: var(--art)"></canvas>
<script type="module">
import { saver } from '/kit/savers/index.js';
const canvas = document.getElementById('saver');
const stop = saver(canvas, 'matrix');
addEventListener('click', () => (stop(), canvas.remove()), { once: true });
</script>
FILES
index.js:saver()andSAVERS, the only door.frame.js: the xorshift, the palette read, the colour parse andrun(), the shared life every saver hangs from.matrix.js: the rain, and the purefell(y, h, roll)reset rule.sleep.js: the bouncer and the purebounce(p, v, max)wall.tiles.js: mrlytiles, the five 2D design rules ported fromcrates/mrlymathtwo::designs, plusseed(design, n),kron(base, level)andtile(design, n, level)giving{ size, cells }over aUint8Array.fractal.js: both fractals, the purefit,autoMaxIter,escaperandwayfind.gl.js: the WebGL2 program: one vertex shader, one fragment shader branching onu_julia, the quad and the uniform table;build()checks every compile and the link and returnsnullon failure, and the fractal then draws nothing.savers.test.js: the pure rules, run withbun test ui/savers/savers.test.js.
- fractal.js4.4 kB
- frame.js3.8 kB
- gl.js3.0 kB
- index.js443 B
- matrix.js1.4 kB
- README.md5.4 kB
- savers.test.js2.8 kB
- sleep.js1.9 kB
- tiles.js1.2 kB