Signal → image
A live field drawn as if it were terrain, and terrain drawn as if it were film. Pressure, cloud and elevation go in; marching squares, splines and a scroll wheel turn them into something that holds still until the world moves.
Working notebook — real-time systems
Instruments, not pictures. Working experiments in real-time systems that turn live signals — weather, time, terrain, language — into images, sound and text. Built in the browser, in the open, in days: each one starts as a written brief, becomes a working piece the same day, and is then revised in public until it holds.
A live field drawn as if it were terrain, and terrain drawn as if it were film. Pressure, cloud and elevation go in; marching squares, splines and a scroll wheel turn them into something that holds still until the world moves.
A composition mixed by where the listener is standing: the position is the score. The next piece in this line listens rather than plays — hearing the world and answering in a voice, on the device, with nothing sent away.
A narrative engine that ran for seventy days and left 2.3 million words, and the archive it left treated as a live object that runs on the clock. Next in this line: the screenplay as a computational object — structure you can compute over, not only read.
A 1980 arcade cabinet reconstructed with no emulator, no engine and no assets — the projection, the models, the typeface and the sound all written by hand, so that every part of the machine is legible. The material of FMX 320, shown working.
One drawing that holds from Mercury to the nearest star — nine orders of magnitude on a single logarithmic line, everything at its measured place, today. The Eames problem, run as an instrument rather than a film.
Common constraint: everything here runs in the visitor's browser, from one file, with no account and no server state beyond a shared hourly weather cache. The larger systems being built alongside this notebook hold to the same rule — nothing leaves the machine that doesn't have to.
Scroll becomes a jog wheel. The page scrubs through 128 frames culled from Mont Blanc 360 Rotation (2013) — a ten-minute full-rotation animation reconstructed from ASTER GDEM elevation data, exhibited at Bleu Acier Gallery in First There is a Mountain. As the scrub passes every second frame, that frame pins into a contact-sheet grid behind the live image: an 8×8 sheet of sixty-four cells at exact 5.625° increments, the entire orbit of the mountain laid flat. Everything derives deterministically from scroll position, so scrolling back up un-builds the montage. In the final stretch the hero frame fades and only the completed compass rose remains.
A browser reconstruction of Moede (2016), a locative-audio artwork for the grounds of the North Carolina Museum of Art. The original iOS app faded a single composition in and out as you walked the site — thirteen GPS zones, each holding one synchronized stem of the same piece, mixed live by your position. Here the museum's satellite map is redrawn with all thirteen real zones as circle overlays; drag the listener across the grounds, or let it tour itself, and each stem's level follows the app's exact distance falloff — full inside the inner ring, silent past the outer — the whole track re-assembling around wherever you stand. The audio is the original 2016 session; the coordinates and radii are the app's own audio.json. Eight synchronized layers became thirteen distinct stems, one per zone.
The contour field from the studio site, given something real to be — and, since 27 August, nothing on it is decoration. The lines are isobars: every hour the page takes sea-level pressure from Open-Meteo at three hundred points on three nested lattices — the contiguous United States, Florida and the Gulf, and the thirty-five-station Tampa Bay grid — samples them with Catmull-Rom splines through the map's own projection, and draws a line every 2 hPa (4 from a distance), the tens heavier and labelled. The field is registered to the map and it holds still: when the hour turns, or fresh data moves it, the lines ease to their new positions over two seconds and hold again. A low in the Gulf reads as rings, and a tropical system crossing the state will read as the thing itself. Every ten minutes the page also samples the bay's thirty-five stations for cloud cover, wind, rain and temperature; cloud cover becomes a faint relief over the bay and the readings feed the panel. The marching-squares tracer is the one that draws the ASTER-derived Mont Blanc wireframes into the greggperkins.com hero — here it is a map, north up at true scale, with the University of Tampa marked by a crosshair. The cursor is a light, in the manner of the star-field on this site's front page: lines near it brighten, holding it still for three seconds burns a hole of clear sky into the cloud that heals over twenty, and passing it over a station reads that station out. The wheel zooms the map — the bay's shoreline from OpenStreetMap in gold, the University of Tampa in red, and, as you pull back to the whole country, the world's coastlines, borders, and the states and provinces of the US, Canada and Mexico from Natural Earth. The pressure comes through the site's own /api/ceiling, one call an hour shared by everyone who is watching.
In the gallery, The Heron — the animated installation in The Bird and The Song (2026) — ran a narrative engine that wrote one short sentence about the heron every twelve seconds, for seventy days, and read it aloud. This is all of it: 247,809 sentences, 2.3 million words, the word heron 114,556 times, from 28 December 2025 to 7 March 2026. The tome runs on the clock. The page picks one of the heron's forty-nine logged days for today and finds the sentence written at this time of day, Tampa time; from there the entries fire at their own recorded intervals, never slower than the gallery's twelve seconds. The timeline is the days, each bar the number of sentences it holds — click one to jump; ← → step; open any day as a chapter and read it whole, every entry timestamped. Nothing is generated here: the engine stopped on 7 March, and what it wrote is what you read.
Atari's Battlezone (1980) — the first-person wireframe tank battle — rebuilt from first principles: no emulation, no engine, no assets. The 3D is a hand-rolled projector with near-plane clipping over a toroidal plain; the tank, supertank, homing missile, saucer, pyramids and blocks are vertex-and-edge lists written by hand after the originals; the score panel is a vector stroke font, drawn in red because the arcade cabinet's monitor was black-and-white behind a red-and-green gel overlay. The mountains, the erupting volcano and the crescent moon hold their compass bearings; the radar sweeps at the top with the enemy as a blip — and, as in 1980, the saucer never shows on it. One shell in the air at a time, on either side. W A S D drives, the trackpad aims the gun in traverse and elevation, Space fires, Esc exits; missiles join the patrol at 3,000 points, supertanks at 10,000, a bonus tank at 15,000. One deliberate anachronism rides along: past 2,000 points, drones — X-frame quadcopters, hollow triangles on the scope — circle high overhead, then commit to a low FPV attack run at a point they lock the moment they dive. Elevate the gun and take them out of the sky, or dodge the locked run and catch them on the climb; standing still is how it ends. The obstacle field is dealt fresh from the date — everyone who plays today patrols the same sector — and the best runs sign the machine in three initials, kept in the browser. The engine hum, the report and the saucer's warble are oscillators, synthesized as the cabinet synthesized them.
An orrery is the old clockwork model of the Solar System — geared planets turning about a brass Sun to demonstrate their relative motions; the name remembers Charles Boyle, the 4th Earl of Orrery, for whom an early one was built around 1713. This one is drawn rather than geared, and in place of gears it has the actual sky: the eight planets, Pluto and Eris stand at the positions they hold today, computed in the page from the Keplerian elements JPL publishes — no API, no library, a hand-rolled Kepler solver and perspective projector in one vanilla file. Space is laid on a logarithmic line — each scale ring is ten times the last — so a single drawing holds from Mercury to Alpha Centauri, nine orders of magnitude, with light-times marked on the rings. Past Neptune everything is real and in its measured place: the belts as statistical fields, the heliopause, Voyager 1 and 2 at the distances they have actually reached — still gaining about three au a year, and they move when you scrub the date — the theorized Oort cloud, and the nearest stars. Drag to turn, scroll to travel, drag the date and the machine turns; click any named body — planet, spacecraft or star — and the eyepiece opens: its true color, its moons turning at their real periods, and three lesser-known facts — scroll deeper and it becomes a page, with the story and three more; or take the ride — one continuous pull-out, Mercury to another star, without a cut. Along the bottom: a timeline of discovery — scrub back and watch the solar system vanish from human knowledge, era by era; every milestone opens into its story — and a second tab, Time, draws the whole discovery of the cosmos as one page — every moment opening into its why, its facts, and the place it points to in Space. And press f to fly — WASD and the cursor, a drive through the drawing itself.
The Orrery turned around. The same logarithmic rail of time runs along the bottom, but the thing that appears as you scrub it is the photographic record itself: before 1858 nothing has been photographed from above and the Earth is only a map — a brass graticule and the cartographer's coastlines on a dark sphere. Then the photographs arrive where they were taken, as photographs: Black's balloon plate over Boston, Nadar's Paris, Lawrence's kite panorama of a burning San Francisco, the Explorer II balloon's first sight of the curve, a V-2 frame over New Mexico, Explorer 6's blur over the Pacific, the first television picture, Gemini's window, and the full discs — DODGE, ATS-3, Apollo 8, Apollo 17 — un-projected onto the hemisphere they show, so the globe wears the latest whole-Earth picture. From 1984 the archive takes over: the Landsat annual mosaic, then from 2000 the day's MODIS and VIIRS tiles, fetched live from NASA GIBS for whichever day the rail is on, every tile bordered and dated so today is a collage of pictures taken today. Around it, every active satellite — more than sixteen thousand — moves at its computed position, propagated in the page from CelesTrak's elements with a Kepler solve and J2 drift; scrub back and the ones not yet launched are gone, until 1957 leaves only Sputnik. Click a photograph or a satellite and the eyepiece opens: its date, its credit, its source, three facts; scroll deeper and it becomes a page. Press f to fly among them, or take the ride: from the station's altitude out past the geostationary ring. The second tab, Time, lays the whole record out as one page.
Storms across the whole planet, starting from Florida. The page opens on the state with Tampa Bay as its reference point and the NHC's live forecast tracks — every advisory point, the cone, the observed track — sorted by distance from wherever you stand. Turn the globe and it becomes the planet's weather: NOAA's GFS model sampled on a 2° grid, ten-meter wind and sea-level pressure at the current three-hour step, redrawn every fifteen minutes, with every region of gale-force wind — Bering Sea lows and Southern Ocean storms included — found and named as a modeled gale area, and GDACS's worldwide cyclone reports placed beside them. Search any city on Earth and the map goes there, with the model's wind and pressure at the nearest grid point even when no storm is near. Close in on the U.S. coast and the instruments turn local: NWS flood and storm-surge alerts for eighteen states, FEMA's mapped flood zones at street scale with a click-to-look-up, USGS topography under them. Take the rail along the bottom and the page runs on the clock: forward eight days through the forecast, back four years through the analyses, and back through every storm the world has recorded — IBTrACS, 13,593 storms and 726,804 fixes from 1842 to this season. Before the record there is only testimony: a dozen storms that exist because somebody survived them and wrote it down. The coastlines, borders and the bay's shoreline are the ones Ceiling draws, loaded from that experiment rather than copied, and drawn its way: nothing filled, the land is its outline in gold. Every feed carries its valid time and fails out loud — an empty feed is never an all-clear, and a failed refresh keeps the previous data and says so.
/api/storms makes a single ERDDAP call per valid time and the edge holds it until the next three-hour step, which also buys a finer reading — the same model at its native half degree over Florida and the Gulf, on Ceiling’s own region lattice, fading into the two-degree field at the edge of its box so no contour carries a seam. The page prefers it and falls back to the JSONP transport it has always used, and the readout names which one drew the field. A live storm’s card now opens 007 Earth at the storm’s own position on the day of the fix, through the camera parameters Earth already accepts. Five things from a reading of the last two revisions are fixed: the distance rings are drawn only where they cross the view, the bay’s shoreline stands down below the zoom its vertices support, the wind under the isobars is interpolated relief rather than a checkerboard of two-degree squares, a witnessed storm inside the record’s era stays chosen when the year loads behind it, and a wind IBTrACS records between the synoptic hours is credited to the track that carries it — so Ivan’s readout and Ivan’s card name the same agency. Revision 5, legible, and the rail settles: the page is denser than the lab’s other instruments and its eleven-pixel readout did not hold, so everything anybody actually reads is a step up — the card’s facts and the reader’s prose to fifteen and seventeen pixels of ABC Diatype, the readout, the caption and the places row to thirteen, and the breakouts under the paragraph to thirteen and a half, with the three hundred storms within two hundred miles of Tampa Bay listed two lines a row, the name in ink and the record beneath it, in a list that scrolls on its own. The card’s column is wider and the box it may stand in is measured off the paragraph and the readout, so a line of a card can no longer run under the field labels. And the rail is a dial rather than a slot machine: while the knob is moving only the knob and the date under it move, and the year, the field, the sentence and the readout wait until it has rested — one timer, so a scrub across a century is one request and not forty. It stands on real steps, a three-hour valid time inside the model’s window and a whole calendar year behind it, because the record’s unit is the season; the witnessed marks are detents the knob catches on, the arrows step one unit and shift steps ten. While the next year is being fetched the year before it stays drawn, dimmed, so the map never goes blank under a moving knob, and a failed request keeps it and still says a failed request is not a quiet year. No card opens and the camera does not chase a storm mid-scrub. Under the line, a scale you can read: a tick every decade back through the record, one a year through the model’s own, labelled only as often as the rail’s pixel width can carry without two years touching. Revision 6, fly: f and the instrument is driven rather than dragged, on the key map 006 Orrery and 007 Earth already have — W A S D over the ground, Q and E for altitude, shift to double it, and the trackpad for the look with no button held. One integrator serves both projections, because the map and the globe are one projection at one scale: on the sphere the camera is a point above it and W walks the point under it along the heading, on the flat map the same key pans north, and climbing past the whole Atlantic turns the map into the globe mid-flight exactly as the wheel does — descending hands it back at the same scale. The readout says the mode and names the bearing, because the sphere is drawn north-up and a heading is otherwise invisible. Flight is a camera and nothing else: the traces stand still as they do in a drag, the sphere samples coarse only while a key is down, no card opens under the controls, the probe goes on reading whatever the cursor is over, and the rail keeps its time. Esc takes flight first and leaves everything else where it was. On a touch screen a thumb on the left half is the stick and the two altitude buttons join the zoom column. Nothing here eases, so anyone who has asked for reduced motion gets the same controls and the same distances. Revision 7, Space | Time: the corner was jammed — a search line, its credit and the zoom column stacked under the header links with no air between them — so the corner is cleared. The two tabs the Orrery and Earth keep, Space | Time, join the header row’s own right group; the zoom column takes Ceiling’s spacing beneath it, clear of the header, with the two altitude buttons still joining it only while flight is on; and the city search leaves the corner for an overlay that / opens over the field, with the same debounce, the same race guard, the same keyboard and the same empty and failed states it had, its Open-Meteo / GeoNames credit moved into the sources. Behind Time is the whole record as one page: one column, oldest at the top, the twelve accounts first and then every year from 1842 to this season as a heading over its storms — each row a name or, where the record never named one, its designation, with the basin, the dates, the peak as its agency gave it with the averaging period, the pressure where there is one, the distance from Tampa Bay, and the agency. Thirteen thousand five hundred and ninety-three rows stay light because every year’s box knows its height before a row of it is drawn: the page is its full length from the first frame and the DOM holds two or three sections — a couple of hundred rows — as the scroll moves through it. It opens on the year the knob is standing in, with that year marked; a filter line narrows it to the storms that came within two hundred miles of the bay, to the ones the record named, or to a basin, and the counts follow; and clicking a row sets the rail to that storm’s first fix and hands it back to Space with the storm selected and its card open. ?tab=time opens it, t is the same door from the keyboard, and Esc returns to the field. Revision 8, the sphere all the way down: flown, the hand-over from sphere to flat map read as a step rather than a descent. The globe’s zoom stopped at 1.23×, so the only way closer was a flip — and it handed over at the disc average while the middle of the screen is π/2 finer, which is exactly how much the same coast jumped as it crossed. So there is no hand-over. The sphere zooms from the whole planet to Tampa Bay, sixty times the planet-filling radius, and past two and a half it stops sampling its texture and is drawn as vectors: the atlas simplified far out and at the flat map’s own full detail near, the bay’s OpenStreetMap shoreline under the same span rule the map uses, the isobars traced in screen space over the visible cap — every 4 hPa, the tens heavy, each labelled once, as on the map — and the tracks, the cones, the gale markers and the reports drawn on the sphere now instead of hidden there, everything culled by one box test against the cap the screen can see. The flat map clamps at the whole world, and the projection changes only when somebody asks: the Globe link, g, F, ◇ or 0. When it does, the scale that carries across is the one at screen center, so the place you were looking at is the size it was. And flight lives on the sphere and turns with you, as it does in Earth: F from the map takes you to the globe at that matching scale, the drawn sphere and every overlay on it rotate so the bearing W is flying stands up the screen (the labels stay upright), W and S run along the look and change the sphere’s own zoom, and Esc leaves you on the sphere where you are with the world settling back to north-up over a second — at once for anyone who has asked for reduced motion. The FEMA polygons and the USGS sheet are the two layers that stay flat, and the readout says where they went. A frame over budget gives up the wind relief first and then half the isobar lattice, and takes both back when the frames come back. Written toward FMX 430.Earth turned over: instead of the planet seen from orbit, the sky seen from the ground. The page opens on the whole dome over Tampa at this instant, south at the bottom — every star the unaided eye can see, 5,044 to magnitude six, each colored from its B−V; the Milky Way as its measured isophotes; the sun, the moon at its true half-degree with its bright limb turned to the sun, and the planets, all at their computed places. Stand anywhere: g opens the Earth from Ceiling's coastlines with the night side shaded for the instant on the rail, and a click or a city's name moves the horizon. Take the rail through five thousand years and the sky turns with the earth's wobble — precession is one matrix applied to the whole catalog, so the pole star of 2800 BCE is Thuban, exactly as Hipparchus, drawn on the rail at 129 BCE, was the first to notice. Drag the date and the hour on the readout and the sky rolls over; the sun's altitude sets the limiting magnitude, so day takes everything but Venus and twilight takes the faint stars first. And as the years pass, the record arrives on the sky where it was found: a guest star between the horns of the bull in 1054, Tycho's star in Cassiopeia, four moons at Jupiter in 1610, Herschel's planet in 1781, the first parallax at 61 Cygni, the first photograph of a star, the spectroscope's single line from the Cat's Eye, a hiss from Sagittarius in 1932, Cygnus A, Cassiopeia A, the first x-ray star, the microwave background, a pulsar in Vulpecula in 1967, the deep field, 51 Pegasi, the kilonova, the shadow of a black hole — sixty-five entries, each drawn with the mark of the instrument that found it, eye to gravity, and each carrying the place and the night. Click one and the eyepiece opens with its altitude from where you stand and the hour it is highest; press stand where they stood and the page takes you to Kaifeng before dawn on 10 July 1054 with the guest star fourteen degrees up in the east, or to a garden in Bath on 13 March 1781. If a thing is under the ground, the clock turns to when it is highest; if it never rises from where you are, the page says so and which way to walk. The second tab, Time, lays the whole record out as one page, eye to gravity.
Every piece is vanilla JavaScript in one file — no framework, no build step — shipped by pull request the day it was briefed and revised in public afterward. Sources are named on each entry: Open-Meteo, OpenStreetMap, Natural Earth, ASTER GDEM, NOAA, GDACS, FEMA, and the original session files. Coding agents work in the loop on a brief written by hand; every piece is deterministic, and everything it shows is sourced.
Students, current or prospective: this is what the work looks like before it is a syllabus — see FMX 430 Spatial Computing and FMX 320 Animation for Interactivity and Games.