The core

The layer every other part stands on. Nothing in it touches the DOM.

Tycho.seed

Seeded randomness. Anything generated or painted draws from these, never Math.random, so a seed always makes the same world and the same picture.

Name What it does
hash(x, y, s = 0) Three integers to 0..1 (non-integers truncated). The painting hash: a place and a seed give a stable value
hash2(x, y) Two numbers to 0..1, multiplying before truncating (Merryfolk’s; the same as hash(x, y, 0) for integers of everyday size)
noise(x, y, s = 0) Value noise from hash, smooth between lattice points, 0..1
makeNoise(seed) A fractal value-noise function (x, y) => 0..1 over a table filled from the seed
smooth(t) Smoothstep
mulberry32(seed) A generator of 0..1
hashInts(...xs) Integers to one 32-bit seed
hashString(s) A string to a 32-bit seed
new Rng(seed) A seeded stream: f(), f(a), f(a, b), i(a, b), pick(list), chance(p), weighted([[w, v]...]), shuffle(list), fork()
fxRand Math.random with Rng’s shape, for effects nothing saves or tests

Each is the games’ own, bit for bit (test/core.test.mjs holds them to the originals): the White Dragon’s hash and noise, Lodestar’s mulberry32, hashInts, hashString and Rng (the White Dragon’s Rng draws the same numbers), Merryfolk’s hash2 and makeNoise.

Tycho.math

clamp, lerp, unlerp, dist, dist2, angleTo, approach(v, target, step), ease(v, target, rate, dt) (closing the gap at a rate per second, independent of the frame rate) and wrap(v, m).

Tycho.color

Hex strings ('#e0701e') and [r, g, b] triples.

Name What it does
rgb(hex), hexToRgb(hex) To [r, g, b] (hexToRgb takes it with or without the #)
rgbToHex(r, g, b) Rounded and clamped
shade(hex, amt) Darker toward black (amt < 0) or lighter toward white
mix(a, b, t), mixRgb(a, b, t) Mixing hex colors or triples
lum(c), css(c) Lightness 0..255; 'rgb(r,g,b)'
rampOf(anchors, n = 9, deep = 0.55, high = 0.3) A painter’s ramp: n tones from soot through the anchors to a warm light, evenly spaced in lightness
tone(ramp, v, jit = 0) The ramp’s tone at v (0..1), moved by a stroke’s jitter
SOOT, WARM The ramp’s ends

Tycho.loop

For a game that draws on animation frames and simulates in fixed steps.

const stepper = new Tycho.loop.Stepper({ dt: 1 / 60, max: 4 });
function frame(now) {
  const dt = stepper.since(now);              // seconds since the last frame, at most 0.1
  if (paused) stepper.hold(); else stepper.advance(dt, step);
  render();
  requestAnimationFrame(frame);
}

advance runs as many fixed steps as the time allows, at most max a frame, then drops what’s left, so a slow phone slows down rather than walking through walls. Samples keeps a rolling window of frame times with percentiles (pct(0.95)), for a readout. Governor lowers the quality while frames run slow: new Governor({ window: 2, below: 40, lower }), then check(dt, active) each frame; every window seconds of active play it compares the frames a second with below and calls lower().