Projects
Five things I've built.
🧰 Selected work · Théo Taburet · signal, images and bicycles
A radio you carry, a map that knows where the water is, a way of hiding a message in the grain of a photograph, a photograph printed in four inks that won’t keep still, and a burst of photos that plays forwards and back. They have less in common than they look, except that each one started as something I wanted for myself.
📻 diapason
Your own radio, broadcast from a phone and played in sync on every speaker around you.
🚲 Ravitools
GPX files enriched with offline water, food and campsite points for long-distance cyclists.
🌾 Natural steganography
Embedding that mimics sensor noise, so the payload is statistically invisible. PhD research.
🖨️ CMYK halftone
A photograph printed in four dithered inks, out of register and trembling, live in WebGL.
🪃 Boomerang
A burst of photos played forwards and back as a looping video, encoded in the browser.
📻 diapason
Your own radio, broadcast from a phone.
One person picks the music, and everyone around them plays it out loud on their own portable speaker, in sync. The phone is the link between the station and whatever speaker it drives, over Bluetooth or a cable. The more people join, the louder and wider the result.
The hard part is not sending the audio, it is agreeing on when to play it. Phones keep their own time and mobile networks are unpredictable, so holding a group tight enough that the speakers fuse into one instead of smearing is where the work went.
You create a station and get a six-character code and a QR. You point it at whatever you want to play, which can be a link, a playlist, your microphone, or just whatever your computer is playing already. Everyone else scans the code, connects their speaker, and they’re in. People who show up late land in time with the rest.
There is nothing to install and no account to create, because it all runs in the browser. It keeps playing when a phone locks its screen or drops from Wi-Fi to 4G. You can let listeners vote on what comes next instead of picking everything yourself.
I built it for bike rides.
RustaxumWebAssemblyTypeScriptAstroOpusWeb AudioPWA
🚲 Ravitools
What’s along the road, even out of signal.
Ravitools enriches GPX files with offline points of interest (water, food, campsites) for long-distance cyclists who lose signal exactly where they most need to know when the next tap is.
Load a route and Ravitools asks OpenStreetMap what lies along it: drinking water, food shops, restaurants, campsites and shelters, toilets, bike shops. It searches a corridor that follows the track rather than a box around it, then keeps each place by its real distance to the line, within a radius you set: narrow in town, wide where there is little. Duplicates are merged only where that still makes sense on the road.
The elevation profile colours every gradient and flags every stretch, longer than a distance you choose, with no water or no shop. What you keep goes out as GPX, KML, KMZ, GeoJSON or an OsmAnd GPX, to load onto a GPS or a phone before you leave.
The demo runs entirely in your browser: the route never leaves it, and the browser asks OpenStreetMap itself what lies along the way. Looking each place up on the web takes a server, so the demo leaves that step out.
TypeScriptReactLeafletGPXOpenStreetMap
🌾 Natural steganography in the JPEG domain
PhD research, 2017-2020
Steganographic embedding that models the sensor noise of a camera, so a hidden payload is statistically indistinguishable from photonic noise that was always there. The work derives a closed-form covariance matrix of the stego signal in the DCT domain. At JPEG quality 100 it carries about two bits per non-zero AC coefficient, and a detector still errs more than 40% of the time (PE ≥ 40%), where a coin toss would err 50%.
PythonMATLABDCTSteganalysis
The scheme splits the 8×8 blocks of a JPEG into four interleaved lattices and embeds into them in order, so that each lattice can be conditioned on the ones already written. Each cell below is a block; hover or tap one to see its reach, everything it depends on, step by step:
Hiding a message in the noise of a camera explains the whole scheme with interactive figures, from the sensor to the JPEG file. See the publications page for the papers.
🖨️ CMYK halftone
A photograph printed by a press that can’t hold still.
Or drop or paste a photo on the print. It stays in your browser.
In 2021 Matt DesLauriers posted a photograph rebuilt from dithered CMYK plates. In a reply he gave the recipe for the image: split it into cyan, magenta, yellow and black, dither each layer down to a bitmap, then draw a circle of that ink on every dot, multiplied over the others. He didn’t say how it moves, and the movement is what gives it depth. I rebuilt it in the browser and measured his video frame by frame until mine moved the same way.
The still image follows his recipe, with Floyd-Steinberg for the dithering. The circles are slightly wider than their cells, as ink spreads, so each plate is dithered a little lighter to make up for it. The colours are those of printing inks rather than screen primaries, on an off-white sheet.
Twelve times a second the whole sheet jolts by less than a dot, and each plate drifts a little on its own. Both are measured off the video. The depth comes from a depth map estimated by Depth Anything, a neural network that guesses distances from a single picture. Each plate is drawn as if seen from its own viewpoint, a little apart from the others and wandering, so near things slide one way, far things the other, and the middle distance stays put. On the video the shift follows nearness to the power 0.3. Every print gets its own press, its shake, register and scratches drawn at random around those values.
Two things went wrong on the way. The first was moiré. Floyd-Steinberg isn’t random: it settles into chains of dots and, around half the ink, into checkerboards, and every plate has its own. Laid over each other, two nearly regular patterns beat into slow waves, which travel as the plates move. Noise on the threshold broke the rows but not the chains. Now the threshold wanders with blue noise, a tile of thresholds ranked so that the cells under any of them are as evenly spread as possible (Ulichney’s void-and-cluster), each plate reading it from a different place. Error diffusion still keeps the tone and the edges, the dots spread evenly, and the plates no longer share a pattern that could beat. The dots also sit a little off the centre of their cells: on a shared grid, how much two plates overlap depends on how far apart they are, and that changes with depth.
The second was at the edges of near things. Moving each dot by the depth under it piled dots up on one side of a flag and tore a hole on the other, and the holes opened and closed as the viewpoints wandered. Now each pixel looks up its ink where the depth under it says, so nothing can pile up and nothing can tear. On its own that lets what is behind eat into the flag’s edge, so the depth map is first grown a little past every near thing’s outline and softened: the flag moves whole, and the plate stretches over what is just behind it.
The Riso sets are two or three risograph inks on cream paper. Cyan, magenta and yellow mean nothing to them, so each set gets its own separation: the amount of each ink that best rebuilds the colour, by least squares on their optical densities.
WebGL2GLSLJavaScript
🪃 Boomerang
A burst of photos, played forwards and back.
Drop in a few photos taken in a burst and Boomerang plays them forwards, then back, as a looping video for Instagram, Reels or TikTok: square, portrait or landscape, with a margin in the colour you choose.
The loop plays live on a canvas while you set it up, and the video is only encoded when you export it. The photos are decoded, turned upright from their EXIF and fitted to the frame in parallel workers, then ffmpeg, compiled to WebAssembly, encodes them in another worker as H.264 the apps take without complaint. It started as a Python script; now nothing is uploaded, and your photos never leave your phone.
The horse at the top of this page is Sallie Gardner at a Gallop: eleven photographs Eadweard Muybridge took in 1878 with a row of cameras the mare set off as she ran past, one of the first bursts ever shot, run through Boomerang.
AstroReactTypeScriptffmpeg.wasmWeb Workers


