Overview
Realness is a rotoscoping tool. It traces a photo into a layered SVG poster - mosaics, shadows, and gradients you can animate. Posters export as SVG, PNG, PSD, video, or GLB. Print the layers on transparency film and stack them the way Disney-era cel animation was made.
Everything runs on your device. No AI, just classical computer vision. Sign in to sync with a small community. What you make uploads right away; the feed catches up on an eight-hour rhythm. Or host your own Realness.
Quick start
Add Realness to your home screen.
- Open the app, tap the camera, and take a picture.
- Realness rotoscopes the photo into a vector poster: five Mosaic layers, four Shadow layers, and up to eighteen gradients pulled from the image.
- Write statements alongside posters to build a thought.
- Sign in with your phone number to sync across your devices.
Interface
The application Island lives at the bottom of your screen.
- Add - file picker for photos you already have. One poster or many.
- 3D - toggle the 3D viewer.
- Camera - quick photo capture. Zoom without losing quality.
- Animation - toggle poster animation.
- Preferences - open the preferences panel.
On desktop, paste anywhere to queue a poster. Same queue Add uses. Keyboard shortcuts are in Preferences.
Thoughts
Thoughts pair statements with posters. Each new entry extends the current thought, so you have 13 minutes to build on what you're thinking.
Statement input
A pill-shaped text field sits at the top of the feed. It's always visible. Tap or click to focus, type your statement, and leave the field to save. The field grows with your text. Press Tab to jump to the first poster in the feed.
A statement written within 13 minutes of a poster attaches to it as an overlay caption, marked by a teal border. Those don't appear separately in the feed. Text-only thoughts stand alone.
Community feed
Thoughts is a shared feed for your instance. A small circle of people, not the whole internet. There are no likes, counts, or comments. When something moves you, you text the person.
Before you sign in, Thoughts shows only the community moderator's posts. Sign in to see the full community feed and add your own.
Top left of Thoughts is a switch with three silhouettes. Red means phonebook mode, the full community feed. Tap it to toggle between the phonebook and a feed of just your work.
The feed moves on an eight-hour rhythm, not a live stream. What you post on this device still goes up right away. Realness is not built to keep you checking back.
Statements
Text entries in the feed, grouped by day. Tap any statement to edit it in place. URLs stay as plain text, not links. Works offline without sign-in, and syncs once you sign in.
Profile
Tap an avatar in the feed to open that person's profile - their posters and statements grouped by day. Download any poster. Profiles are cached offline, and visits are not tracked.
Posters
Posters appear in Thoughts and on profiles, grouped by day. Use Add or Camera on the island, or paste on desktop.
Anatomy of a poster
Each poster is a layered SVG.
Mosaic - transparent cutouts from photo contrast. Five layers: Boulders, Rocks, Gravel, Sand, Sediment. In 3D view the finer layers are raised, which reads as depth.
Shadow - tonal bands, lightest to boldest: Light, Regular, Medium, Bold. Background is an optional fill behind them.
Stroke - outlines along the shadow paths.
Gradients - eighteen colors from your photo, coloring the shadows.
Interactive features
- Poster details - long-press a poster on touch, or click it on desktop, to expand its caption area. Your own posters show overlay statements, download, and set-as-avatar. Other people's show their profile chip and a download button.
- Animation - GPU color transitions. Toggle and cycle speed from Preferences.
- Drama - front and back light bars. Toggle and cycle combinations from Preferences.
- Mosaic layers - on touch, press and hold to highlight a layer. On desktop, hover to highlight the layer under the cursor.
- Fit or fill - long-press toggles between meet (poster inside the frame) and slice (poster fills the frame). On desktop, click.
- Pan - in portrait slice mode, swipe left or right to move the poster within the frame.
- Aspect ratio - cycle presets from Preferences on desktop.
- Slice alignment - nudge the crop in slice mode from Preferences on desktop.
- Storytelling - switch the feed to horizontal scroll from Preferences on desktop.
- Presentation - a keyboard shortcut fills the screen with the poster and hides the interface. Press again to exit. The key is listed in Preferences.
- Set as avatar - on your own posters, the poster details area sets that poster as your profile avatar.
Subjects
Your own posters have a mask pen, the ✎ in the poster menu, for grouping mosaic cells into subjects. A subject is a thing in the picture: a face, a flower, the foreground. Opening the pen turns mosaic on if it was off.
Press a cell and drag to grow the selection through cells of a similar tone. Press an already-selected cell and drag to erase. A cell belongs to one subject at a time. Pinch to zoom in.
The panel lists subjects with a color, a name you can edit, and a two-tap remove. + adds another. Subjects save with the poster and come back when you open it again.
3D viewer
Toggle it from the island or Preferences. With 3D on, tune spread, opacity, camera, atmosphere, and motion in the preferences panel.
Mobile - drag to pan. Tilt follows your finger and device gyro, when allowed.
Desktop - drag for parallax. Hold a modifier while dragging or scrolling to pan. Scroll with the primary modifier to zoom. Arrow keys tilt. The exact keys are listed in Preferences when 3D is on.
Vector work runs in web workers. View state persists locally.
Download and export
Open the poster menu first: long-press on touch, click on desktop. A blue sweep border means an export is running.
Exports clone the live poster on screen. There is no separate render path.
- SVG - matches what you see: visible layers, opacity, crop, drama. Any size. Gradient stops export as HEX so Illustrator, Affinity, Inkscape, Figma, and Sketch read the colors.
- PNG - the same flat snapshot at 3840px wide.
- PNG layers - one file per layer for compositing, rather than one on-screen snapshot.
- PSD - separate layers for printing: Shadows (Background, Light, Regular, Medium, Bold), Stroke, Mosaic (Sediment through Boulders). 1920px on touch, 3840px on desktop.
- Video - H.264
.mov, 24fps, up to 4K, animating at the speed your animation preference is set to. Drag an audio file (mp3, wav, ogg, m4a, flac) onto the poster to bake that track in as the soundtrack, uncompressed. - GLB - 3D model for Blender and similar tools. Uses the open 3D viewer when it is on, and otherwise builds from the poster.
Visualizer for a song
Want a visualizer for a song? Drag the track onto a poster. No video editor, no timeline.
- Drop a track on a poster. Any audio file: mp3, wav, ogg, m4a, aac, or flac. Drag it onto the bare poster tile, no menu needed. It decodes on your device and nothing is uploaded.
- The video runs exactly as long as your song. The poster animates for the whole track, however long it is. It does not stop at the end of a cycle and hold.
- Audio is baked in, uncompressed. The exported H.264 4K
.movcarries your track as PCM, so it arrives in your DAW without a second round of lossy encoding. It plays anywhere, and drops straight into Ableton Live, Resolve, Premiere, Final Cut, or YouTube.
A blue sweep border and a frame counter on the poster show progress while the export runs.
Set the animation preference before exporting. Motion speed and layers are the pace of the video, the same motion you are watching on the poster. With no audio, the video is one full cycle at that speed. The poster is vector, so the 4K rasterize stays crisp however you scale it.
Poster driver
Scripts load /poster-driver in a hidden Chromium window. The page exposes window.poster_driver.render(data_url), which traces a photo the same way the app does and returns SVG and PNG without saving the poster.
From the repo, after npm run build:
CHROME_PATH=/path/to/Chromium npm run poster -- photo.jpg
The script serves dist/, not src/. If you changed tracing code, rebuild first. Otherwise the run waits two minutes and reports that the driver never became ready. Set CHROME_PATH. Each run takes one photo. Files land in artifacts/poster-driver/ as SVG and PNG, named from the poster id.
You need the repo, Node, and a Chromium browser. Tracing stays on that machine.
Converting a video
In the app, Video export animates one poster. From the harness, make:animation traces each frame of a clip and encodes the posters as an mp4, with the clip's soundtrack. It runs from the brayness harness root - no Realness checkout needed.
npm run make:animation clip.mov
You can pass --fps N (default 24), --workers N (default 6), --width N (0 keeps the traced size), --crf N (0-51, default 23; lower is sharper and larger), and --keep-frames if you want the raster PNGs after encode.
You need ffmpeg and a Chromium browser. Set CHROME_PATH, or have Brave, Chrome, Chromium, or Edge installed. The script opens https://realness.online/poster-driver unless REALNESS_URL points at a local serve.
The mp4 is artifacts/animation/<name>.mp4. Traced SVGs stay in artifacts/animation/<name>-frames/. If you stop a run and start again, those SVGs are reused. Raster PNGs are deleted after a successful encode unless you pass --keep-frames. Audio is remuxed as AAC at 192k. If the clip has no audio, the mp4 has none either.
Printing a cel animation
Posters are built for transparency. Mosaic cutouts stay clear, and shadow and gradient layers stack like colored gels, made to shine through on a light table or projector. Each printed sheet is a cel. Register a stack of them and you get the layered look the hand-painted studios used.
There are three ways to print it, depending on how much of that layering you want to do by hand.
Print the whole poster. Export PNG or SVG and send the full poster to one sheet of overhead film. Every layer arrives already composited. One cel, the fastest path, and the closest to what's on screen. Choose this when you want a finished image on transparency without assembling anything.
Print each layer. Export PNG layers or PSD and print one layer per sheet of transparency. PSD keeps them named and ordered for you: Shadows (Background, Light, Regular, Medium, Bold), Stroke, then Mosaic (Sediment through Boulders). This is the path for registration. Trim to a common edge, or use registration holes, so each sheet drops into the same position.
Stack all of it together. Print the layers. Print the background on paper, which gives the shadows something to sit on. Register the transparencies on top of it in order, lightest shadow to boldest, mosaic on top. Light it from behind on a light table or projector and you are looking at a hand-built cel. The layers read as depth, and you can lift, swap, or re-shoot single sheets the way an animation stand does.
A laser printer suits the film best: sharp vector edges, opaque color where you need it, clear where you don't. Inkjet transparencies work in a pinch. Let the ink dry fully before stacking.
How posters are made
No AI. Classical computer vision, on your device, rather than machine learning or generative models. Realness doesn't do the hard part of tracing itself. It stands on two open-source projects that have spent years getting it right, and owes them the credit.
- vtracer, by the visioncortex team, turns photo contrast into the mosaic cutouts. The five mosaic layers are vtracer's color-region tracing, tuned for the look Realness is after. It does the work that makes the stained-glass quality of a poster possible.
- potrace, by Peter Selinger, is the starting point for the shadow layers. The tonal bands (Light, Regular, Medium, Bold) and the strokes along them began as potrace and have since been heavily rewritten. Turning a bitmap into clean, hand-inked curves is Selinger's foundation. What grew from it is its own thing. Realness is GPL-2.0 because potrace is; the license has the required notices and source.
Both are the real engine of a poster. Realness is the darkroom around them: capture, layering, color, and export.
Sign-on
Sign in with your phone number and a display name. You join this instance. Your posters and statements show in Thoughts alongside everyone else.
Realness is one instance per moderator, a small community of their people. You can sign in here to try it and take part. The real community is the one you build by hosting your own, for your studio, family, union hall, or circle. See Project below for setup and docs.
Realness is invisible to search engines. No tracking, no advertising. The server handles auth and storage only, and your data lives on your device.
Account
Sign-in required:
- Sign off - sign out.
- Name - display name.
Install
Realness is a Progressive Web App. Install it and it runs like a native app: offline, with full keyboard shortcuts and desktop GPU. The walkthrough below matches your device. Open Other devices for the rest.
Add Realness to your dock
Works like a native app, straight from the web. macOS · Safari
Other devices
iPhone & iPad
Android
Chrome · Edge · Brave
macOS Safari
Sync
Sync runs when you return to the app or come back online.
What goes up
Posters, statements, profile changes, and anything you saved offline on this device upload as soon as you have a connection.
What comes down
Realness checks for new work once every eight hours, not more often. Open the app at noon and again at three and you will see the same feed. Open it the next morning and you will see what posted overnight.
That check brings in statements from your other devices, plus new profiles, statements, events, and posters from everyone on the instance. Your name and avatar are verified every visit. The rest waits for the eight-hour mark.
Slow on purpose. No live ticker, no reason to refresh every few minutes. Open the app, catch up, make something, leave.
When sync runs, changed profiles and statements refresh and the feed reloads. A blue sweep border around the app means sync is in progress.
Status borders
Border around the app:
- Blue sweep - syncing, exporting, processing, or refreshing.
- Yellow - offline, editing paused.
- None - online and idle.
A teal border is different. It sits on a single poster rather than the app frame, and marks attached caption statements. See Thoughts.
A Realness of your own
The Realness source code is available. This page documents the tool. The repo has setup, deploying your own, and deeper background to explore.
- README - overview, local dev, Firebase deploy.
- Philosophy - why moderators, small communities, and client-first design.
- Architecture - storage, sync, offline-first, serverless stack.
- Contributing - branch workflow, tests, pull requests.
- Verify a release -
npm run verifyconfirms a live instance matches a GitHub release.
Prompt an agent
Copy a ready-made prompt to give an AI agent. Paste it into Cursor, Claude Code, or any coding assistant — it will know how to guide you through deploying your own Realness instance.
Preferences
Layers, geology, motion, 3D atmosphere - every poster responds to what you turn on. Reset everything to start clean.
Changelog
v2.9.3 - September 7, 2026
Google could not make sense of the site - the homepage never said which URL it was, so a link with tracking junk on the end looked like a second homepage. The license page was listed in the sitemap and blocked in the same breath. And /privacy answered like a real page, then moved you to the terms page once the app loaded, which a crawler never sees. It is a redirect now.
- Tracing a poster no longer needs a build -
npm run posterdrove a copy of the site it started itself. It uses the deployed site, the wayposter:videoalready did. PointREALNESS_URLsomewhere else to override.
- Tracing a poster no longer needs a build -
v2.9.2 - September 2, 2026
Signing in on a phone gets past the captcha - when Google decided to ask for the picture puzzle, the puzzle opened behind the sign-in window and there was no way through. The window used to sit in the browser's top layer, which paints over everything regardless of stacking. It is a normal window now, with its own scrim and Esc to close, and on a phone it takes the whole screen instead of floating as a card.
A downloaded poster is the drawing, nothing else - the PNG used to come out with the realness icon burned into a corner.
Making a movie from a poster moved to the harness -
npm run make:animationfrom the brayness root does whatrender-poster-video.jsdid inside this repo. The driver page and the documentation say so, and the prompt the page hands a coding assistant points at the new script.Posters say what they are in a way other readers can follow - the microdata types were relative paths, which only made sense to us. They are full
https://realness.online/...addresses now. Posters saved before this still read fine.
v2.9.1 - August 22, 2026
Other people's thoughts show up again - a contact's writing was cached the first time you read it and never checked again. Signing out and clearing the app was the only way to see anything new they wrote. Sync now checks each contact's thoughts on its own, rather than waiting for something about their profile to change - posting does not change a profile, so it never did.
A sync that fails tries again - the eight hour clock was set before the work, not after. A tab closed mid-check, or one file erroring, and the app believed it had caught up and waited another eight hours.
Everyone has a profile from the moment they sign in - a person who never edited their name had no profile file at all, so the phonebook could only show their phone number. Same for thoughts: nothing was written until you wrote one, and every read of them asked for a file that was not there.
The poster driver page explains itself - it used to apologise for being undressed. It now shows how to call it, how to trace a movie from the terminal, and hands over a prompt you can paste into a coding assistant.
v2.9.0 - August 18, 2026
Turning mosaic or a layer off lets it leave - it used to vanish on the same frame. It now fades out, then goes. The menu and grid do the same, and they stay in the page so they can; the geology layers still unmount, one beat later.
A layer key with mosaic off now shows that layer alone. It used to do nothing you could see. The group comes back carrying just that layer. Background stays; it is the ground the others sit on.
Arrow keys pan the poster instead of jumping by slice. Hold to pick up a little speed. Home still centres.
Mosaic still builds up when you turn the group on - cutouts arriving one by one used to look like five separate presses, and the stagger dropped. The entrance now holds for the whole arrival.
The sign-in window keeps still while it works - it used to shrink around the three dots between steps, leaving them crowded against the close button. The window now holds its size, the dots sit in the middle of it, and they are the same clay as its border instead of looking like a second button.
v2.8.6 - August 16, 2026
- The name field says "Name" once - it used to say it twice, as a heading and again in grey inside the box. The grey one is gone, and the heading is now what a screen reader announces.
v2.8.5 - August 16, 2026
Signing in no longer takes over your settings - the account page was two different pages wearing one address: a phone number form when you were signed out, your settings when you were signed in. Signing in now happens in a window that opens over the page, so the settings are always where you left them - and folder sync, which never needed an account, is there whether you sign in or not.
Wipe is a real button - clearing what Realness keeps in this browser used to be a small grey word tucked under the sign-in form. It sits with the other settings now, and it asks before it clears anything.
v2.8.4 - August 16, 2026
- Your avatar comes back - while the poster listing was wrong, every failed look-up was written down as "this file does not exist", and that note outlived the fix. Realness now forgets those notes the moment it hears from storage what you actually have.
v2.8.3 - August 16, 2026
Posters you made on another device show up again - the listing of your posters was being rebuilt from whatever this device happened to have on hand, and nothing ever asked storage what was really there. Work made on your phone stayed on your phone. It now asks, and mends itself the next time you open Realness.
Avatars are not blank circles - same cause. The list that says where older posters were filed away was being wiped, and an avatar is usually one of the oldest posters you have, so it could not be found.
v2.8.2 - August 16, 2026
- Ask an AI to help you run your own Realness - the ready-made prompt now sits on the License page and in the documentation, under "A Realness of your own", with a word about what it is. It used to be an unlabelled button on the pricing page.
v2.8.1 - August 16, 2026
- Terms and pricing now match the license - Realness is GPL, so you can already run it, change it, host it and use it commercially without paying. The paid tiers are for the written agreement procurement asks for, and to keep the project going.
v2.8.0 - August 16, 2026
Your posters can be lava lamps - the layers drift between shapes, slowly, never quite repeating. It leans on the GPU, so it stays off until you ask: turn on Animate, then Lava lamp under it, in Preferences or on the About page.
Name the things in your picture - the mask pen (the ✎ in the poster menu) groups mosaic cells into subjects: a face, a flower, the foreground. Press and drag to grow a selection through cells of a similar tone, press again to erase. Subjects now save with the poster and come back when you open it, each with its own color, a name you can edit, and a two-tap remove. Opening the pen turns mosaic on if it was off.
Pinch to zoom works on posters again. Swiping between them is unaffected, and no longer eats the tap on posters that already fit the screen.
Tapping the caption no longer toggles the poster switch - the switch's label had grown to cover the 3D viewer and the whole caption, so a tap meant for a subject button or a rename field landed on the switch as well.
Your own posters show up when you haven't signed in.
The license is GPL-2.0 and now says so -
/licenseused to redirect to the pricing page. It's a real page now with the copyright notice, the no-warranty disclaimer, where to get the source, and the full text. The Terms link to it instead of describing a license we don't use.The 3D viewer got its lighting redone - drama lights and single-ink strokes. Behind an opt-in preference, it can also texture the model with the live, morphing poster instead of a still.
v2.7.4 - August 9, 2026
You can make posters from a whole folder of photos now - point
npm run posterat a directory instead of one image. Add--formats svg,png,psd,glbto pick what comes out. GLB is new to the command line, so a 3D model no longer has to come from the menu.Batch renders were writing empty posters - if a poster's layers had not finished saving, the file still came out perfectly well formed with nothing in it, and nothing noticed. A render now waits for every layer to be stored, checks the tracer found something, and names the frame when it did not. Only one poster is drawn at a time, so two can no longer be half-drawn at once.
v2.7.3 - August 6, 2026
Video renders were leaving frames blank - the renderer photographed each frame before its poster had finished drawing, and a blank frame still writes a perfectly good file, so nothing caught it. It waits for the poster now and will not write an empty frame. Renders also carry the clip's audio and work at any width.
The pricing page would not scroll on a phone - swiping sideways moves between tiers, and a thumb scrolling down drifts sideways as it travels. So every scroll changed the tier, and changing tier put you back at the top of the page. A swipe now has to travel further across than down, and moving between tiers keeps your place.
v2.7.2 - August 6, 2026
Posters now render for a whole video, not just a single still -
npm run poster:video <clip>extracts every frame of a clip, traces each one in a pool of parallel headless browsers, and stitches the traced frames back into an mp4. Frames already rendered are skipped on rerun, so an interrupted batch resumes where it left off instead of starting over. The tracing runs against the deployed site (override withREALNESS_URL), so a render never depends on a local build being present or current.A screen reader announced something that is not there - the feed ends in an invisible marker that asks for the next page of history when you reach it, and it was meant to be hidden from assistive technology.
aria-hiddenwritten with no value is the empty string, which means nothing at all, so the marker was read out like any other item in the feed. It saystruenow.
v2.7.1 - August 5, 2026
The footer island sat too close to the bottom edge - it was pinned one base-line off the viewport. It now sits at one and a half.
Statements stopped years short of the beginning - they archive into whole sections saved under a timestamped filename, and that timestamp is also one of the statements inside. Asking for the page id therefore found that one statement, so a page of history read as a single sentence and everything behind it stayed shut. The section still carries the plain
/+author/statementsid, so realness asks it for its list. Every archive opens, back to the first thing you ever wrote.Posters never paged past the first screenful on a profile - a poster coming into view is what asks for the next page of history, and the line listening for it was written so that Vue ran the handler once while drawing the page and threw the result away. Nothing was ever told a poster had been seen, so nothing ever asked for more. Both feeds reach the oldest poster now.
A thought that came into view three times fetched the same page of history three times - a day can bring several thoughts on screen in one frame, and each read the list of pages already seen before any of them had written to it. The duplicate statements then collided as list keys and the feed stopped drawing where they landed. One page loads at a time per person, and a statement that arrives twice is only kept once.
The oldest thoughts would not open - a thought written alongside a poster is drawn as that poster's overlay rather than on its own, so there was nothing to report it had come into view, and coming into view is what asks for the next page of history. The oldest thoughts are exactly the ones riding under posters, so history stopped where they began. The poster now speaks for the thought it carries.
The synced folder was missing everything before the newest page of statements - it read the index and stopped, the way the feed used to. It walks every archived page, so
notes.mdholds the whole library.Thought folders repeated the year in every name - they already sit in a folder named for their year, and the current year is the one you are standing in.
2019/December 8, 2019 Sunday morningis now2019/12-08 Sunday morning. Still sorts by date; the part that varies is easier to find.
v2.7.0 - August 3, 2026
Exported video did not move the way the poster does - the export reimplemented SVG animation rather than reading it. It parsed
dur,values,keyTimesandkeySplinesoff each<animate>and solved the easing curve itself, so every frame was a second, independent guess at what the browser was already drawing, and the two drifted apart. Each frame now seeks the poster's own timeline and copies what the browser computed there -animValfor geometry, computed style for paint. Easing,keyTimesand repeats come out exactly as they look on screen, because they are the same numbers.Video exports ran at a fixed crawl no matter what the animation preference said - the export pinned its timeline to
crawland sampled three poses a second, holding each for four ticks of the 24fps file and cross-fading between them to cover the gap. It renders one frame per tick at the speed you set now, so the video is paced like the poster in front of you. With no audio dropped on it, the video is one full cycle at that speed.A song longer than one animation cycle froze on the last pose - covering the extra time meant wrapping the render clock back to the start of the cycle by hand, and anything the wrap did not account for held still until the track ended. The animations repeat indefinitely on their own, so the export just keeps seeking and the poster keeps moving for the whole song.
Your track was re-encoded on its way into the video - audio was muxed as 192 kbps AAC, so a file that had already been through a lossy encoder took a second pass before it reached your DAW.
.movcarries PCM natively, so the decoded audio is written through untouched.Exporting a poster that had scrolled off screen produced a blank video - posters are marked
content-visibility: auto, which lets the browser skip rendering one that is not on screen, and the export rasterized those skipped frames as empty. The poster is held visible for the length of the export.The favicon was a grey smudge in the tab - the mark is a mosaic, and at 16 pixels a single tile is about a pixel and a half wide, so the tiles blur into each other and the whole palette averages out to grey. Only the favicon and Safari's pinned tab draw the mark from the top of
icons.svg, so that drawing now crops tight and fills flat, with the palest tiles darkened enough to hold their shape against a dark tab strip. The mark everywhere else in the app is untouched.Pressing the realness mark left it stuck on its solid fill - a press snaps the six tiles together and swaps the mosaic for flat colour; letting go is supposed to fade back. The rewind that resets the drift animations collects
getAnimations(), which hands back CSS transitions alongside keyframe animations, so it caught that fade one frame in and paused it at frame zero. Only keyframe animations get rewound now.The 3D and grid preferences lost their icons -
preference.vue'siconprop was renamedshow_icon(asiconshadowed the imported<icon>component) and two of the three call sites in the menu still passed the old name, so the attribute fell through to the fieldset instead.
v2.6.7 - July 31, 2026
- Nobody could see anybody else - v2.6.6 closed a hole that let anyone read your follow list, and in doing so took away permission to list the directory of people, so realness could only show you your own posters and thoughts. Reading the directory is allowed again, for signed-in people only. Your follow list stays private, and the phone directory is no longer readable by anyone who is not signed in.
v2.6.6 - July 31, 2026
Your follow list was readable by anyone - the rule meant to keep
relations.html.gzprivate compared a whole path against a filename, which never matches, so it never excluded the file. Storage rules grant access if any one of them says yes, and that single permissive match handed the list to any reader, signed in or not. Every rule that can reach the file now excludes it by name, andnpm run test:ruleschecksstorage.rulesagainst the storage emulator before every commit.Crawlers were locked out of the assets that render the site -
robots.txtdisallows/and allows named paths back, and/assets/and/fonts/were never on that list, so the CSS, JS, and fonts behind every page we do want indexed were off limits, along with the OG images. All four are allowed now.Your thoughts and posters are on screen the moment you open realness - the feed used to wait for the whole contact list to come down from the server before it drew anything, even though your own work was already on the device. It draws what it has first, and other people fill in behind it.
Anything not yet on your device arrives in half the trips - fetching a poster asked the server where the file was, then asked again for the file. Those turn out to be the same request, so realness remembers the answer.
Scrolling a big feed stopped stuttering - every person in your feed made realness read through its entire local store, once each. Posters keep six layers apiece, so that added up fast on a long history. It reads once now and shares the answer.
Coming back to the tab is immediate - leaving and returning kicked off filing and cleanup work that had nothing to do with what you were looking at. That runs on its own schedule now.
The border only lights up when realness is talking to the server - it used to come on for anything that took a moment, including work happening entirely on your device, so it never told you much. The feed has its own quiet spinner for loading.
A new poster no longer rebuilds the whole feed - any change used to reload every person on screen. Realness now knows what moved and refreshes just that.
Reloading no longer re-reads everyone you follow - your own profile, thoughts, events and posters reconcile every time realness runs, so anything you make on another device still turns up right away. Reading back through other people's files is the part that runs on a schedule, and a page reload was resetting that schedule every time.
v2.6.5 - July 28, 2026
- Only the homepage was indexed - Firebase appended a trailing slash to
/about,/docs,/pricingand/terms, so every sitemap URL answered with a 301 and each prerendered page'srel=canonicalpointed back at the redirecting URL. Search Console had one valid page.trailingSlash: falseserves the prerendered HTML at the canonical URL. - Pressing an icon turned it into a blob - a rule written for picking out poster layers matched every icon in the app, so holding the gear down thickened its outline until the teeth filled in. The rule only reaches posters now.
- OG image is a real poster now - a hand-picked 1280x960 jpg replaces the generated card across
index.html, the prerender defaults, and the JSON-LD image. - OG candidates generated from the admin's posters - an unlisted
/og-candidatesroute mounts each landscape poster and cuts two 1200x630 frames: full bleed, and the same poster under the marketing card copy. A driver script servesdistand works the route in headless Chrome over the devtools protocol - no new dependencies, reusing theCHROME_PATHthe score script already needs.npm run og:pickinstalls a chosen frame aspublic/og.jpgafter checking its dimensions. npm run shipverifies the version it just cut -verifydefaults to the newest GitHub release carrying a manifest, so ship was checking the previous release rather thanpackage.json's version.
v2.6.4 - July 25, 2026
- Download menu had no surface over a poster - the poster footer styled its menus with a descendant selector and then reset
background/backdrop-filteron anything nested, which caught the download sheet along with the author row. Footer menu rules are scoped to direct children now, and the bar's glass moved to a::beforeso the bar stops being a Backdrop Root - nestedbackdrop-filterwas sampling the bar instead of the poster, leaving the sheet's blur dead. - PNG, PSD, and layer exports cropped the poster edges - every raster path clones the live
<svg>, which carriespreserveAspectRatio="xMidY… slice"to fill its grid cell.slicescales to cover the target canvas, so it can only ever crop; export clones now setxMidYMid meetto fit the whole viewBox. - Download menu rebuilt - formats are real
<button>s (focusable, Enter activates) in a column sized to its labels; the per-layer PNG export moved from an unlabelled icon buried in the corner of the PNG button to its own chip alongside it; sheet and chip slide up on the poster grid's easing curve.
v2.6.3 - July 24, 2026
- Favicons and OG image matched to the bone-background icons -
192.png,512.png, andog.pngwere still generated on the old dark surface; the generator now renders them onbonewith dark text.
v2.6.2 - July 24, 2026
- Storytelling mode trapped touch users - the poster
<svg>hardcodedtouch-action: pan-y, blocking the horizontal swipe storytelling scrolls on; and the footer nav (the only way to reach Preferences and turn storytelling back off) unmounted whenever storytelling was on, leaving no touch-reachable escape since the toggle is keyboard-only (w). Fixed both: posters allow horizontal pan while storytelling, and the footer stays mounted.
v2.6.1 - July 22, 2026
- Homepage was blocked from indexing -
robots.txtdisallowed/with no exception for the homepage itself, andsitemap.xmlnever listed it. AddedAllow: /$and the homepage entry so Google can crawl and index it.
v2.6.0 - July 21, 2026
- Build no longer rewrites
public/sitemap.xml- every build regenerated it with today's date, leaving a perpetual uncommitted diff after each deploy.dist/sitemap.xml(what actually ships) is unaffected;public/sitemap.xmlstays as the static checked-in copy until next touched deliberately.
v2.5.13 - July 21, 2026
- Folder sync rebuilt - File System Access API directory handle persisted in IndexedDB, mutex-guarded queue mirrors thought/poster saves and deletes as human-readable files (date-titled thought folders, snippet-named poster SVGs/PNGs) instead of raw itemid dumps. New
sync_svgpreference toggles SVG export. - Account page rebuilt - Sync folder section (choose/re-sync, live progress, Brave detection with a how-to-enable dialog) and notifications now both live under Account, visible ahead of sign-in status resolving.
base-line.stylsplit - constants-only now; the actual reset/root-properties/fluid-type-scale CSS output moves to a newreset.stylloaded once fromindex.styl.- About hero fade - tagline and hero poster now fade/rise in with the rest of the hero instead of snapping in or sitting blank; stagger tightened so the reveal settles quickly.
v2.5.12 - July 17, 2026
- Verifiable releases - Keep a changelog
## Unreleased(hidden on/docsuntil cut);npm versionpromotes and tags,npm run shippublishes build-manifest + notes to GitHub. Copy points skeptics at the ritual. - realness-design realign - views/components drop invented class hooks for markup state, rename
--on-emphasis→--contrast, align with design skill conventions. - Markdown as an element stylesheet - content typography moved from Stylus mixin into
src/style/elements/markdown.styl. - Notification opt-in onboarding - once per device after sign-in, a modal offers push when the instance supports it.
- Mosaic logo smalti wobble - clipped fragment paths on each tile, paced off the glint cycle, for more physical glitter.
- Preferences markup tidy - notifications fieldset rename, tweakpane slide simplified to attribute selectors; orphan
grid.svgremoved. npm run verify- deploy checksum CLI defaults clarified; docs updated for independent GitHub-manifest checks.- Test coverage surge - ~2k net test lines across posters, profile, views, sync/persistence, potrace, and utils.
v2.5.10 - July 13, 2026
- A pass over the whole codebase looking for real bugs - storage, components, workers, the tracer and the 3D viewer, read end to end. It turned up saves that could collide and lose each other, a sign-in that could hang, a tracer that could mix one image into another, keyboard handling that failed in the compose box, memory the 3D viewer never gave back, and an upload queue that could stall. Every fix carries a regression test.
- Cloud archive consistency - partial poster archive failure now rolls back cleanly instead of leaving orphaned files.
- Pricing page rewritten as real HTML - tier content hand-authored per semantic-HTML convention instead of generated from JS data.
- Poster video export overhauled - fixed ~8x-too-fast playback, cross-fade frames, 1440p/14 Mbps encode.
v2.5.9 - July 11, 2026
- Materials-and-roles design system - palette converted to OKLCH, materials as static CSS custom properties with derived weights, new
--inforole. Automated contrast/harmony guardrails. /colorsview - live palette renderer in-app with swatches, roles, geology demo, Depth demo.- Stylus → native CSS migration - element styles that don't need mixins moved to plain
.css. - Shared
focus-ring()mixin - fixed silently dropped keyboard focus indicators. - Realness mosaic logo - placeholder glyph replaced with mosaic mark: palette colors, smalti masks, per-tile drift, color cycle, press-to-flatten via Web Animations API.
- Pricing split into per-tier carousel -
/pricing/:tierdrives tier nav, prev/next, touch swipe, all synced to URL. - Switched to GPL-2.0 - closed-source-license gap closed; name/logos trademarked outside GPL; commercial tiers as support contract.
- Drag-and-drop image upload - photos and SVGs dropped onto the app queue like paste; SVGs resize through the vectorize path.
- Small fixes: dark-mode compose caret,
--basalt-transparentreference,clamp()order bug, button/checkbox style leaks, Colors header, Buy Button iframe reflow, TOC not tracked in git.
v2.5.8 - July 4, 2026
- Instance capabilities - runtime
/capabilitiesprobe discovers optional features (push, phone integrity); falls back to shippedcapabilities.json(all off). Firebase auth split into its own vendor chunk. - Deleted posters stay gone - cached posters no longer resurrected;
remove_missing_posterpurges HTML, shadow, geology layers, and cached directories on delete. - Root indexable - home route dropped
noindexfor search engines. - Thoughts shell drops after mount - static
<h1>removed from DOM once Vue mounts. - Preload Lato Light - closes FCP→LCP gap from font repaint.
- Dated poster labels - poster SVGs announce "Poster from
" instead of generic "Poster".
v2.5.7 - July 4, 2026
- Static LCP shell for home route -
index.htmlrenders<h1>Thoughts</h1>in#appat first HTML parse, gated by pathname. Placeholder<header>reserves real header height. LCP 2.0 s, performance 93, accessibility 100, CLS 0.002. - Poster SVG labels -
role="img"+aria-label+aria-roledescription="poster";aria-labelon avatar toggle, messengersms:link, delete button. Lighthouse accessibility 74 → 100. - Fonts cached a year -
/fonts/**max-age=31536000, immutable. - Deleted posters drop from visitor feeds - missing loads no longer render blank figures.
July 3, 2026 - Removed EXIF metadata feature
- EXIF capture + overlay removed - iOS Photos picker strips camera/date/GPS before our code runs. Deleted exif files, preference, and capture code. Rationale:
docs/monopoly.md.
July 2, 2026 - Performance
- Lazy vectorize on first paint -
use_vectorizeloads via dynamic import afterrAF; boot imports ~80KB lighter. - Deferred
init_serverless- Firebase auth after Vue mount to not block first paint. - Trimmed preconnects - removed 8 unused preconnect/dns-prefetch links; only
firebasestorage.googleapis.comremains. - Lazy InstallGuide + PreferencesMenu -
defineAsyncComponentfor 242KB video and preferences panel. - CLS shell -
#appgetsmin-height: 100dvhto prevent layout shift on mount. - Stable feed render -
Thoughts renderedimproved from 4.2s to ~2.7s.
July 2, 2026 - Support layout, build-time TOC, scroll & swipe
- Support layout - site-nav rendered once by
support-layout.vuereplacing 5 hand-rolled copies. - Build-time TOC -
scripts/generate-toc.jsprecomputes heading trees; views import static arrays instead of runtimemarkdown_toc(). - Swipe-back & scroll - removed
scrollRestoration = 'manual'; TOC links userouter-link replace;scrollBehaviorhandlesto.hashwith smooth scroll. - Mobile TOC UX - larger sub-level fonts,
touch-action: manipulation, headingscroll-margin-topfor safe-area. - Pricing page - padding fixes, space between buy button and actions, removed redundant price text.
July 2, 2026 - Account, performance
- Require a name - nameless users redirected to
/account; validation on save and sign-on. - Lazy 3D and download -
defineAsyncComponentfor viewer and download in poster figures. - Smaller first feed page -
optimize()caps each author's directory toSIZE.MAX(55).
July 2, 2026 - Discoverability & social previews
- Open Graph cards - marketing URLs prerender with full
og:*and Twitter Card tags. og.png(1200×630) - social image with headline, value prop, CTA.- Meta tag pass - titles, descriptions,
og:image:alt,twitter:image:alt. - Sitemap & robots - crawlable marketing pages in
sitemap.xml; app shell unindexed. - Static docs -
public/documentation.mdandpublic/llms.txtfor crawlers and LLM discovery.
July 1, 2026 - Early in the v2.5.7 cycle
- Stripe buy buttons wired into $100/$500 pricing tiers.
- Mask subjects - named path groups with grow-select/erase (WIP).
- 3D mode poster menu gesture - reveal poster menu in 3D with same gesture as SVG mode.
v2.5.6 - June 29, 2026
- Blank duplicate-poster avatars fixed - visibility-aware canonical election.
- PSD export - stroke stripped from shadow fill layers.
- Statement text selectable; touch callout suppressed on tappable elements.
- "Copy prompt" agent buttons inline in docs and pricing.
- SSR hardening - prerender and standalone guards;
var(--base-line)in viewport-relativecalc()s.
v2.5.5 - June 28, 2026
- Account overhaul - inline sign-in, web push notification preference, Stripe reduced to CTA.
- Pricing and legal pages - ToS simplified (arbitration removed), LICENSE updated,
security.txtrefreshed.
v2.5.4 - June 17, 2026
- Install guide - native install prompt, platform detection, synthetic walkthrough animations (HyperFrames) for iOS/Android.
v2.5.3 - June 15, 2026
- Big simplification - removed phonebook, relations, events, picker; simplified profile/poster menus; expanded docs.
- Preferences menu - hints, icons, keybindings; animated silhouette toggle for
only_minefeed filter. - About page refresh with gallery cap and lore.
- Archive location map with self-healing
sync:index.
v2.5.1-2.5.2 - June 11, 2026
- Prerendered marketing pages and public site structure.
- About page rebuilt - integrations, feature list, balanced gallery; archive loading fixes.
- Frosted-glass styling consolidated; account UI polish; 3D "haze" renamed to atmosphere.
v2.5.0 - May 31, 2026
- Mask pen drawing tool debuts.
May 16, 2026 → 05-25 - v2.4.x: 3D posters land
- 3D poster viewer and export tooling migrated in from
projects/3d; scene motion refactored with tests; device orientation handling; iOS touch/orientation polish.
2026-04 → 05 - v2.3.x: platform
- Vite+ toolchain transition.
- Stripe sponsorship integrated; profile account hero; homescreen icon management.
- One poster on the page - deduplicated rendering; caching and performance work.
2026-02 → 04 - v2.0-v2.2: the Thoughts interface
- Interface re-centered around Thoughts; navigation removed; color scheme green → blue.
- Safari feed performance; off-screen cutout removal; feed ordering fixes.
- Sync folder for poster output export; download-video improvements; landscape poster swipe.
- Local dev setup simplified.
2025-10 → 2026-02 - v2.0.0: the rebuild
- Offline/anonymous poster creation synced after sign-in; cutouts and shadows stored for later sync; PNG and PSD export alongside SVG; layer naming; dark-mode color-scheme declared.
2024 → mid-2025 - v1.5-v1.8: the on-device tracer
- vtracer (wasm) integrated - on-device color-region tracer behind mosaic layers.
- Offline actions merged with sign-in sync; iPad standalone detection; preferences and docs growth.
2017 → 2023 - origins
First commit December 20, 2017. Realness grew up as a phone-number-identity social PWA - profiles, avatars, statements, events, phonebook - with dark mode by 2018 and steady releases through v1.x. The poster emerged as the core creative object through experiments from 2018 to 2022. Most of that social surface was shed in 2026 (v2.5.3) to focus on posters and thoughts.