a puzzle is a function awaiting input.
ppuzzle.dev is a generative puzzle constructor. We treat every grid, lattice, and constraint network as a small program — a deterministic system that resolves to one state and one state only when its arguments converge.
construct()
shapes from rules
A puzzle begins as an empty domain — a grid of undefined cells, a graph of unconstrained nodes. We seed it with a small set of axioms and let propagation do the rest. Difficulty is not authored; it is a consequence of the constraint topology.
// generate a 10x10 lattice const lattice = Array.from({ length: 100 }, (_, i) => ({ row: Math.floor(i / 10), col: i % 10, phase: (i * 80) + 1000, state: 'undefined' }));
- > deterministic seed produces deterministic puzzle
- > constraint set is editable as a JSON record
- > rendering is decoupled from solving
- > every cell exposes its own animation phase
solve()
convergence as motion
We do not animate the answer; we animate the process of arriving at it. A solver is a sequence of rotations applied to a state vector. When the vector stops moving, the puzzle is solved. The visual on the right is one such solver running in real time — 100 cells, 100 phases, one shared coordinate system.
The moiré that emerges between adjacent cells is not an effect — it is the by-product of a slightly mis- aligned period. The same phenomenon that ruins a halftone print is the one that gives the lattice its living quality.
/* the only line of JS that touches the grid */ window.addEventListener('scroll', () => { document.documentElement.style.setProperty( '--scroll-progress', window.scrollY / document.body.scrollHeight ); });
observe()
the lattice as a clock
Read the grid the way you would read a clock made of gears. Each cell turns at its own rate. Patterns travel diagonally. Hue drifts as you scroll, because hue is bound to the same scalar that drives every other animation in the page.
iterate()
five passes of refinement
- 01 seed choose a hash and project it onto the lattice
- 02 propagate walk every constraint, mark every implication
- 03 rotate advance each cell by its own per-index phase
- 04 render composite the lattice over the void background
- 05 observe scroll — the only input the solver listens to
manual()
operating the page
Move the document. The grid responds to
window.scrollY; nothing else. There are no
cookies, no forms, no calls to anywhere outside this
runtime. The page is a closed system. If you want a
different image, scroll to a different position.