# Keith Reed > Full-stack developer. I'm a former teacher. I started writing software for my tutoring students and kept going. Now I build web apps, APIs, and data tools for other people too, mostly in TypeScript and React on Cloudflare. This page lays the projects out on a {7,3} tiling of the hyperbolic plane, drawn in the Poincare disk. The center cell is the introduction; the seven cells around it are six groups of projects and a plain list; each group's projects sit just outside it. The canvas draws nothing that is not also in the files below. ## Projects ### Learning software Practice apps and teaching tools, most of them built for my own students. - [CalTPA Writing Studio](https://impossible-spaces-landing-v2.pages.dev/api/projects/caltpa.json): A writing workspace for California teaching candidates working on the CalTPA. Each part gets its own template, and a keyword check shows which rubric categories a draft still misses. Live: https://caltpa.rootsofreason.org. Drawing in its cell (an illustration, themed on the project and not its code): Seven moving boundaries and the polygon that satisfies all of them at once, found by clipping a bounding region with each half-plane. A boundary that was out of the way starts to shape the polygon, another stops, and the best vertex for a slowly turning objective moves along an edge to the next. The pointer pulls the nearest boundary toward it, turns the objective when swept around the center, and shifts the preferred point. A picture of competing requirements; the writing tool does not measure a draft this way. - [Math Mastery](https://impossible-spaces-landing-v2.pages.dev/api/projects/math-mastery.json): Math practice I built for my tutoring students: multiplication, division, fractions, geometry, and word problems, each with a visual model next to the numbers. Live: https://math.rootsofreason.org. Drawing in its cell (a working model, themed on the project and not its code): Exact dissections that keep their area. A rectangle shears into a parallelogram, a triangle is cut off its left end and slid to the right end, and the same material is a rectangle again; other scenes cut a rectangle into its largest squares, then the largest squares of what remains, by the Euclidean algorithm. Every piece is a polygon with exact coordinates, and the pieces always add up to the area they started with. Moving the pointer along the base shears and cuts; resting on the remainder brings the next cut. - [Code Playground](https://impossible-spaces-landing-v2.pages.dev/api/projects/code-playground.json): A coding sandbox for students where a shape, a set of sliders, and the code that draws it stay in sync. The code can be shown in Luau, GDScript, or Python. Live: https://code.rootsofreason.org. Drawing in its cell (a working model, themed on the project and not its code): Plane transforms that do not commute. A short program of rotations and translations, kept as a list of operations, moves one angular glyph; a faint second glyph runs the same operations in another order and ends elsewhere. The program R T R^-1 T^-1 returns the glyph to its orientation but not its position, and running its inverse retraces it. The pointer changes the translation and the rotation angle. - [Physics flashcards](https://impossible-spaces-landing-v2.pages.dev/api/projects/study-cards.json): Interactive flashcard modules for physics. Each card starts with something you move, then names the idea and asks a few questions about it. Live: https://lms.relay70.dev/cards/. Drawing in its cell (a working model, themed on the project and not its code): Vibration modes of an ideal circular membrane clamped at its rim. Ten modes, each a damped oscillator with a Bessel-function shape, answer a slowly sweeping drive or the pointer in proportion to their shape at the pushed point, so a push on a nodal line does nothing. The drawing shows the nodal lines and two displacement contours at a slowed timescale. An idealized study, not a calibrated instrument. ### AI infrastructure Tools I use to run my own AI-assisted work. - [auto.relay70](https://impossible-spaces-landing-v2.pages.dev/api/projects/auto-relay.json): Tools that keep my AI coding agents working on a schedule while I'm away, and keep a record of what each run did. Live: https://auto.relay70.dev. Drawing in its cell (a working model, themed on the project and not its code): A small deterministic discrete-event model: a job splits into two branches that run on two services, waits at a join until both have finished, continues on a third service, and is acknowledged once. Events are ordered by time and then by arrival order, a service takes one job at a time, queues are bounded, and one job in four fails once and is retried. A slow spell on a service makes the other branch wait at the join, and the wait spreads back toward the start. The pointer lengthens the service nearest to it. A toy model, never a live view of the agents the project runs. ### Interactive design Instruments, drawing tools, and navigation experiments you can play with. - [Lithorium](https://impossible-spaces-landing-v2.pages.dev/api/projects/lithorium.json): The site for my rock and mineral business. The home page is the menu: five sections orbit a ring of drifting particles. Live: https://lithorium.rocks. Drawing in its cell (a working model, themed on the project and not its code): A Penrose-type rhombus tiling from de Bruijn's pentagrid construction: five families of parallel lines, one rhombus for every crossing. Two internal offsets, not the screen position, decide which tiles exist. They drift slowly and follow the pointer; the pattern stays still until an offset crosses a critical value, then a row of tiles rearranges while the rest holds. A stroke occasionally runs along one ribbon of tiles, and a five-fold vertex shows its pentagon when the pointer rests near it. A visual study of quasiperiodic order, not a claim about the shop's minerals. - [Sound Toys](https://impossible-spaces-landing-v2.pages.dev/api/projects/sound-toys.json): Ten instruments that run in the browser. They all color sound the same way: pitch sets the hue, register the saturation, and loudness the brightness. Live: https://sound-toy-deploy.pages.dev. Drawing in its cell (a working model, themed on the project and not its code): Nine coupled phase oscillators, the Kuramoto model. Each has its own natural frequency; with weak coupling the phases drift past one another, stronger coupling lets neighbors lock and then the whole ring, and when the coupling relaxes it comes apart with single oscillators slipping a turn. Each phase is a mark on a radial support. The pointer strengthens the coupling near it, detunes an oscillator when swept past, and offers a steady reference rhythm when held still. Silent here; the project itself makes sound only on request. - [Chromata](https://impossible-spaces-landing-v2.pages.dev/api/projects/chromata.json): A drawing tool that redraws a picture as moving lines. Small pathfinders wander across the image and draw with the colors they pass over. Live: https://chromata.relay70.dev. Drawing in its cell (a working model, themed on the project and not its code): Pathfinders cross a picture they cannot see all at once, in the manner of Michael Bromley's Chromata. At each step a pathfinder weighs the color ahead against its own, the direction of the picture's edges, how much of that ground is already covered, and any barrier, and takes the heading with the lowest cost. Covered ground draws no darker, so effort moves to what has been missed. A slow pointer stroke leaves a soft barrier the pathfinders bend around; resting opens a quiet window. The picture is computed and never changes. ### Tools & data Small apps that do one job. - [Focus Matrix](https://impossible-spaces-landing-v2.pages.dev/api/projects/focus-matrix.json): A study-session planner. You list what you're going to do, give each item a timer and a priority, and earn a coin for each one you finish. Live: https://focus-matrix.pages.dev. Drawing in its cell (a working model, themed on the project and not its code): One-dimensional optimal transport. A fixed amount of material lies along one curved rail and has to be moved onto another rail according to a changing demand. The monotone map sends the share of material below a point to the point with the same share below it on the other rail; sixteen ribbons carry equal shares. Demand moves between one broad concentration and several narrow ones, so the ribbons spread, gather, split and merge, and one concentration can only grow if the others give up material. The pointer near the lower rail asks for more there. A picture of allocation, not the planner's scheduling. ### Commerce & websites Storefronts and sites for businesses, including my own. - [Roots of Reason website](https://impossible-spaces-landing-v2.pages.dev/api/projects/roots-of-reason.json): The website for my tutoring practice. Families read about the services, book a consultation, and fill out intake forms there. Live: https://rootsofreason.org. Drawing in its cell (a working model, themed on the project and not its code): Space colonization, an algorithm for growing trees: resource points are scattered above and below a ground line, each point attracts the nearest branch tip in reach, and a point is used up when a branch comes near it, so branches compete for room and growth redirects when a region is spent. The pointer shades the crown, which new growth avoids, or adds resource points underground. The tree persists; pruning is gradual and bounded. A stylized growth model, not plant physiology. - [Onleigh Crystals](https://impossible-spaces-landing-v2.pages.dev/api/projects/onleigh.json): The store for my crystal business. I built the current website, and I wrote a Shopify theme from scratch for when the shop moves to Shopify. Live: no public link. Drawing in its cell (a working model, themed on the project and not its code): Geometric optics on a faceted outline. Five rays refract into the crystal at index 1.5 (an authored display value, not a measurement of quartz), reflect inside when the angle is too steep, and leave through whichever face the geometry sends them to. The emitter drifts slowly; the pointer sets its position and direction. ### Research & models Pictures computed from real math. - [Long Night](https://impossible-spaces-landing-v2.pages.dev/api/projects/long-night.json): A night sky drawn from real star positions for the current time, above a treeline. The same positions are available from an API. Live: https://night.relay70.dev. Drawing in its cell (a working model, themed on the project and not its code): The bright stars over Los Angeles for the current time, placed from catalog positions and sidereal time on a stereographic chart with the zenith near the center and the horizon on the ring of pines. Occasionally a constellation is traced in stages, a coordinate arc such as a circle of equal altitude or the celestial equator is drawn, or the track a star will follow over the next three hours appears with ticks at one, two and three hours. The pointer tilts the chart a few degrees, which moves the zenith but not any star in the sky; it shows the altitude and azimuth of a star it is near, and holding still on a star draws its track. The sky is recomputed from the clock; the drawing animation does not speed it up. ## How the page works - Coloring: each unlit cell takes the color of the lit cell it is fewest steps from; light and dark bands count those steps. - Rim: each point of the boundary circle is colored by the region nearest to it from the current view. - Pathfinders: walkers on the tiling edges choose the direction whose cells match their own color, after Chromata by Michael Bromley. - Opening a project: clicking or scrolling into an open cell loads the project in a frame when its site allows it, and goes to the site otherwise. - Drawings: each project cell holds a small mathematical model that the page runs itself. The models are studies themed on the projects, not the projects' code or data. Each keeps its state when the pointer leaves, advances in fixed time steps, and reports named events. The pointer changes the conditions of a model; it does not move the drawn objects directly. - Controls: Pause stops every drawing. Explore drawing lets a keyboard or touch user move a stand-in pointer inside the opened drawing. With the reduced-motion setting on, the drawings do not move by themselves and respond only while the pointer is in them. ## Machine-readable endpoints - [/llms.txt](https://impossible-spaces-landing-v2.pages.dev/llms.txt): This summary as plain text. - [/api/index.json](https://impossible-spaces-landing-v2.pages.dev/api/index.json): This list. - [/api/projects.json](https://impossible-spaces-landing-v2.pages.dev/api/projects.json): Every project: text, links, screenshot, how the page opens it, its cell, and a description of its drawing. - [/api/projects/.json](https://impossible-spaces-landing-v2.pages.dev/api/projects/.json): One project, including the full source of its drawing. - [/api/groups.json](https://impossible-spaces-landing-v2.pages.dev/api/groups.json): The six groups and the plain-list cell, in ring order. - [/api/layout.json](https://impossible-spaces-landing-v2.pages.dev/api/layout.json): Tiling parameters, every lit cell, sites, and cell vertices out to ring two. - [/api/regions.json](https://impossible-spaces-landing-v2.pages.dev/api/regions.json): All 2400 cells with their region and step count. - [/api/pathfinders.json](https://impossible-spaces-landing-v2.pages.dev/api/pathfinders.json): The walker rule and a seeded snapshot of every trail. - [/api/stars.json](https://impossible-spaces-landing-v2.pages.dev/api/stars.json): The star catalog behind the Long Night cell. - [/api/sky](https://impossible-spaces-landing-v2.pages.dev/api/sky): Live: star altitudes and azimuths now, or ?at=ISO-time&lat=&lon=. - [/api/source.json](https://impossible-spaces-landing-v2.pages.dev/api/source.json): Every source file with its purpose and exports. - [/gallery.html](https://impossible-spaces-landing-v2.pages.dev/gallery.html): Developer view: each drawing alone, with controls for detail, motion, sky time and pointer path. - [/list.html](https://impossible-spaces-landing-v2.pages.dev/list.html): The same projects as an ordinary web page. ## Source - [js/art/director.js](https://impossible-spaces-landing-v2.pages.dev/js/art/director.js): Page-wide attention budget across the twelve drawings. It never edits a model. It answers claim(id, seconds) when a module's slow driver asks to start an optional excursion, scales how - [js/art/gallery.js](https://impossible-spaces-landing-v2.pages.dev/js/art/gallery.js): Developer gallery: each drawing module at its own size, driven by the same adapter the page uses, with a fixed seed, an injected clock and UTC, selectable detail and motion, and scripted pointer - [js/art/index.js](https://impossible-spaces-landing-v2.pages.dev/js/art/index.js): The twelve drawings, each a stateful module behind the page's drawing signature. - [js/art/input.js](https://impossible-spaces-landing-v2.pages.dev/js/art/input.js): Pointer features for one drawing: position in the drawing's own units, smoothed velocity, speed, dwell, winding about the center, and time since the pointer left. The tracker sees a position - [js/art/math/affine.js](https://impossible-spaces-landing-v2.pages.dev/js/art/math/affine.js): Plane transforms as homogeneous 3x3 matrices acting on column vectors, stored as the six numbers [a, b, c, d, e, f]: x' = a x + c y + e, y' = b x + d y + f (the canvas matrix layout). - [js/art/math/bessel.js](https://impossible-spaces-landing-v2.pages.dev/js/art/math/bessel.js): Bessel functions of the first kind with integer order, and the zeros that fix the vibration modes of an ideal circular membrane clamped at its rim. - [js/art/math/contour.js](https://impossible-spaces-landing-v2.pages.dev/js/art/math/contour.js): Marching squares: line segments of the level set of a scalar field sampled on an n by n grid that covers [-1, 1] in both directions. NaN samples mark points outside the domain. Segments - [js/art/math/halfplane.js](https://impossible-spaces-landing-v2.pages.dev/js/art/math/halfplane.js): A convex region as the intersection of half-planes, found by clipping a bounding polygon one half-plane at a time. Each output vertex carries the label of the line its outgoing edge lies on. - [js/art/math/optics.js](https://impossible-spaces-landing-v2.pages.dev/js/art/math/optics.js): Ray tracing through a convex polygon of one refractive index in air: ray-edge intersection, Snell refraction, total internal reflection. Pure functions, no drawing. - [js/art/math/pentagrid.js](https://impossible-spaces-landing-v2.pages.dev/js/art/math/pentagrid.js): Penrose-type rhombus tilings by de Bruijn's pentagrid method. Five families of parallel lines, family j perpendicular to e_j = (cos 72j, sin 72j) degrees, with line offsets gamma_j. Every - [js/art/math/sky.js](https://impossible-spaces-landing-v2.pages.dev/js/art/math/sky.js): Star catalog, sidereal time, horizontal coordinates, and a stereographic view of the sky. Celestial calculations stay apart from the display projection: the projection can tilt without - [js/art/math/util.js](https://impossible-spaces-landing-v2.pages.dev/js/art/math/util.js): Small numeric helpers shared by every drawing module. No DOM, no canvas. factor for an exponential approach with time constant tau, valid for any step size - [js/art/modules/auto-relay.js](https://impossible-spaces-landing-v2.pages.dev/js/art/modules/auto-relay.js): auto.relay70: a small deterministic discrete-event model of a job with two branches that must both finish before it continues. place of each node on the cell, and the edges between them (quadratic curves) - [js/art/modules/caltpa.js](https://impossible-spaces-landing-v2.pages.dev/js/art/modules/caltpa.js): CalTPA Writing Studio: a polygon that stays inside seven moving boundaries (half-plane intersection). slack of each boundary at the drift clock, before the pointer's adjustment - [js/art/modules/chromata.js](https://impossible-spaces-landing-v2.pages.dev/js/art/modules/chromata.js): Chromata: pathfinders cross a picture, steered by color, edge direction, coverage and barriers. After Michael Bromley's Chromata. the picture the pathfinders cross: sky, sun, two hills, water - [js/art/modules/code-playground.js](https://impossible-spaces-landing-v2.pages.dev/js/art/modules/code-playground.js): Code Playground. Plane transforms do not commute: rotate-then-translate and translate-then-rotate end in different places. A short program of rotations (R) and translations (T), kept as a list of - [js/art/modules/focus-matrix.js](https://impossible-spaces-landing-v2.pages.dev/js/art/modules/focus-matrix.js): Focus Matrix: one-dimensional optimal transport, a fixed amount of material moved from one rail to another. target settings: weight, center and width of three concentrations, as the picture steps through them - [js/art/modules/lithorium.js](https://impossible-spaces-landing-v2.pages.dev/js/art/modules/lithorium.js): Lithorium: a Penrose-type rhombus tiling built from a pentagrid; its offsets drift, and the tiling rearranges in rows at discrete moments. rebuilds the patch for the current offsets; tiles that persist keep their fade state - [js/art/modules/long-night.js](https://impossible-spaces-landing-v2.pages.dev/js/art/modules/long-night.js): Long Night: the bright stars over Los Angeles for the current time on a stereographic chart, with the zenith near the center and the horizon on a ring of pines. The pointer tilts the chart. Stars - [js/art/modules/math-mastery.js](https://impossible-spaces-landing-v2.pages.dev/js/art/modules/math-mastery.js): Math Mastery: exact dissections that keep their area, as rectangles are cut and reassembled. pieces of the shear dissection of a b by H rectangle: the top edge slides by s u, the triangle - [js/art/modules/onleigh.js](https://impossible-spaces-landing-v2.pages.dev/js/art/modules/onleigh.js): Onleigh Crystals. Geometric optics on a faceted outline: five rays from one emitter refract into the crystal (index 1.5, an authored display value, not a measurement of quartz), reflect inside - [js/art/modules/roots-of-reason.js](https://impossible-spaces-landing-v2.pages.dev/js/art/modules/roots-of-reason.js): Roots of Reason. Space colonization: resource points are scattered in a crown region above a ground line and a root region below it. Each growth step, every point attracts the nearest branch - [js/art/modules/sound-toys.js](https://impossible-spaces-landing-v2.pages.dev/js/art/modules/sound-toys.js): Sound Toys: nine coupled phase oscillators on a ring, the Kuramoto model. coupling matrix for a global scale and optional per-oscillator local factors - [js/art/modules/study-cards.js](https://impossible-spaces-landing-v2.pages.dev/js/art/modules/study-cards.js): Physics flashcards. The vibration modes of an ideal circular membrane clamped at its rim: ten modes (Bessel function in the radius, cosine or sine in the angle), each a damped oscillator. - [js/art/render.js](https://impossible-spaces-landing-v2.pages.dev/js/art/render.js): Drawing frame shared by every module. Modules draw in normalized units: the center of the cell is (0, 0), its mean vertex radius is 1, x runs right and y runs down. Stroke widths are given in CSS - [js/art/runtime.js](https://impossible-spaces-landing-v2.pages.dev/js/art/runtime.js): Adapter between a stateful drawing module and the page's drawing signature step(ctx, w, h, t, dt, io) - [js/art.js](https://impossible-spaces-landing-v2.pages.dev/js/art.js): Public drawing interface of the page: ART maps a project id to a factory for its step function step(ctx, w, h, t, dt, io). The modules live under js/art/. - [js/content.js](https://impossible-spaces-landing-v2.pages.dev/js/content.js): generated from relay70 portfolio registry/catalog.json and site.json (entries with preview true, minus impossible-spaces) - [js/dossier.js](https://impossible-spaces-landing-v2.pages.dev/js/dossier.js): the project's text, set in the margins around the opened cell - [js/field.js](https://impossible-spaces-landing-v2.pages.dev/js/field.js): The coloring of the plane and the pathfinders that walk it. - [js/hyper.js](https://impossible-spaces-landing-v2.pages.dev/js/hyper.js): Poincaré disk machinery. Complex numbers are {re, im}; isometries are SU(1,1) matrices [[a,b],[conj(b),conj(a)]] acting z -> (az+b)/(conj(b)z+conj(a)). - [js/layout.js](https://impossible-spaces-landing-v2.pages.dev/js/layout.js): Where everything sits on the plane. Used by the page and by the build that writes /api, so both describe exactly the same layout. - [js/main.js](https://impossible-spaces-landing-v2.pages.dev/js/main.js): a group brought to the middle is enlarged on a phone so its project names fit words for the cell nearest the middle: the introduction at the center, a group's summary on a group - [js/tiling.js](https://impossible-spaces-landing-v2.pages.dev/js/tiling.js): {p,q} tiling generator for the Poincare disk. center of the circle through u and v that meets the unit circle at right angles - [js/view.js](https://impossible-spaces-landing-v2.pages.dev/js/view.js): the group colors are chosen for paper; on the dark ground they are lifted to one lightness