[07]COLOPHON> man wesli.devbuild #5f67501

The stack, the ASCII pipeline, the swarm that carries page changes, where the live data comes from, and what the site promises about speed and access. The numbers on this page come from the repository or from your browser.

evolution
0034
last commit
05 OCT 2026 22:01
built
05 OCT 2026 22:01
output
static HTML, one server function

[01]> cat package.json

Static HTML from Astro, no UI framework. The browser gets a handful of small TypeScript modules, and every page reads fine with JavaScript off.

runs in your browser
rolewhatversionlicense
frameworkAstroStatic HTML from .astro components. No UI framework.7.3.5MIT
stylingTailwind CSSEvery colour, size and duration is a token in one @theme block.4.3.3MIT
motionGSAPEntrances and the scramble. DrawSVG loads on the home page only.3.15.0GSAP standard (no charge)
scrollLenisSmooth scrolling, loaded on your first scroll input.1.3.26MIT
renderingCanvas 2DPortrait, network, swarm, globe and page transitions.—web platform
searchPagefindFull-text index behind Ctrl K, built after the site.1.5.2MIT
typeJetBrains MonoThe only typeface. See Type below.5.3.0OFL-1.1
builds and hosts it
rolewhatversionlicense
imagessharpTurns photos and renders into art maps; draws the social cards.0.35.5Apache-2.0
sitemap@astrojs/sitemapEvery indexed page, without the shielded ones.3.7.4MIT
languageTypeScriptClient modules, build scripts and the one server function.6.0.3Apache-2.0
QAPlaywrightScreenshots, overflow and smoke checks in headless Chrome.1.63.0Apache-2.0
hostingCloudflare PagesStatic files, plus one Pages Function for Talk to Atlas.—service

[02]> npm run art

Nothing on this site is a picture of ASCII. Photos and renders become small data maps at build time; the browser turns those into glyphs on a canvas, cell by cell.

  1. [01]source

    scripts/input/

    A photo of me for the home portrait; renders and SVG drawings for the rest. None of them ship to the browser.

    out .webp · .svg

  2. [02]map

    npm run portraitnpm run art

    sharp crops each source, tunes its contrast and packs it into a small PNG: red is tone, green is the subject mask, blue a palette index.

    out 11 maps · 429 KB

  3. [03]sample

    lib/ascii/sample.ts

    Per cell: average tone and mask, then contrast, gamma, histogram equalisation and a little dither. Pure code, so it also renders the no-JS fallbacks.

    out a 12-glyph ramp

  4. [04]atlas

    lib/swarm/atlas.ts

    Every glyph is drawn once per colour and tone into a single sprite sheet, so nothing calls fillText while it animates.

    out 13 colours × 8 tones

  5. [05]canvas

    Canvas 2D

    A frame is a run of drawImage() copies from the atlas. Settled art is a still canvas: no loop runs until something moves.

    out zero work at rest

R tone
Brightness on its own. The black backdrop stays blank.
G mask
What counts as the object: # inside, · outside.
B palette
Where the art’s one hue lives; the dots stay neutral.
= glyphs
All three combined: 819 lit cells, one particle each.
fig.01The colophon art map (200 × 339 px) at four points of the pipeline, sampled at 44 × 43 cells at build time. The same file and sampler draw the art at the top of this page.
~/wesli.dev — regenerate the maps
# photo → map, then render the no-JS fallback from the same map
npm run portrait
# renders + SVG drawings → art maps + src/lib/swarm/art-meta.ts
npm run art
# 10 art maps, 394 KB together; the portrait map is 35.2 KB

[03]> cat page-transition.ts

Every lit cell of an art is one particle. When you follow a link, whatever ASCII is on screen breaks apart and flies to where the next page keeps its art.

  1. 0 mssampleWhatever ASCII is on screen is read back into particles: position, palette and tone per cell (at most 2,600 on desktop, so it reads as a swarm, not noise).
  2. 0 msfadeThe page content fades. The sticky nav stays: it is the one piece of chrome that never leaves.
  3. 0–700 msflightEach particle bursts outward, then follows a shared arc towards where the next page puts its art. The leading edge peels off first.
  4. 462 mshandoffThe cloud is written to sessionStorage and the next document is requested. Hover prefetch has usually fetched it already. A handoff older than 4 s is ignored.
  5. first paintredrawAn inline script on the new page draws the cloud before anything else paints, so the swarm never blinks out between documents.
  6. 0.9–1.5 sassembleThe page’s swarm takes the cloud and flies it into its own art, cell by cell. Surplus particles fade on landing, missing ones fade in.
Swarm budgets per layout
layoutviewportglyphmax colsparticlesflight
desktop≥ 1600 × 8607 px1707,0000.9–1.5 s
laptop≥ 1200 × 5606.5 px1305,5000.9–1.5 s
tablet≥ 640 wide6 px1104,0000.9–1.5 s
mobile< 640 wide5 px722,4000.8–1.25 s

the layout this browser is in now

Source and target cells are both sorted top to bottom and paired by rank, so a shape flows as a whole instead of dissolving into dust. Each particle follows a cubic curve: a burst outward, an arc it shares with the stream, a landing point that is re-read every frame, so scrolling mid-flight still lands. Reduced motion, a hidden tab or nothing ASCII on screen: the link just navigates.

[04]> curl -s api.open-meteo.com

Four readouts on this site change on their own: two in your browser, two at every build. None needs a key, and each has a plain fallback when its source is down.

WEATHER

, in your browser
place
Utrecht, 52.09 / 5.12
key
none: a free, open endpoint
cache
30 min in sessionStorage, 4 s timeout
offline
the line says “sky offline” and moves on
now
Utrecht--°Csky pendingasking the window

EVOLUTION

, at build time
source
git log of this repository
number
the commit count, oldest = 0001
shield
subjects naming shielded work are rewritten (4 so far)
fallback
a committed snapshot, for shallow CI clones

FWRD RELEASE

, at build time
reads
version and date of the newest release, nothing else
timeout
5 s, then the last cached release
now
v2.73.5 · 05 OCT 2026 (fetched this build)

CAFFEINATE

, in your browser
source
a model, not a sensor
input
my daily coffee rhythm and the clock in Amsterdam
decay
each cup absorbs over ~27 min, then halves every ~5 h
clock
NL
now
caffeinate: --

The only secrets belong to Talk to Atlas: the model key and its anti-abuse settings. They are Cloudflare secrets, read server-side by one Pages Function, and never part of anything your browser downloads.

[05]> performance.getEntries()

The budget is what I hold every page to. The readout is what this page actually loaded in your browser, measured with the browser's own APIs and sent nowhere.

budget

home
JavaScript ≤ ~90 KB compressed
other pages
JavaScript ≤ ~60 KB compressed
at rest
no animation loops: canvases draw only while something moves
layout shift
≈ 0: art boxes are sized up front, fallback fonts are metric-matched
on demand
smooth scroll on first scroll, the palette on first use

heaviest data, measured at build

font
39.5 KB, one file for every weight
art maps
394 KB for all 10; a page loads only its own
portrait
35.2 KB, home only

measured in your browserneeds JavaScript

JavaScriptcompressed, external files
—
CSScompressed
—
this documentcompressed HTML
—
requestsdocument included
—
largest paintLCP
—
layout shiftCLS, summed
—

script budget— of 60 KB

[06]> cat a11y.md

The target is WCAG 2.2 AA. This is what that means on this site, criterion by criterion.

  1. 1.4.3Text contrast of at least 4.5:1. The dimmest text colour on the site measures 5.4:1 against the background.
  2. 2.4.7A visible focus ring on every control, in the green that otherwise only means “live”.
  3. 2.4.1A skip link first, then landmarks (header, main, footer) and one h1 per page.
  4. 2.1.1Everything works from the keyboard: the palette, the menu, dialogs, the image lightbox and the Atlas chat.
  5. 2.1.4Single-key shortcuts can be switched off, here or from the palette. Ctrl K always works.
  6. 2.5.5On touch screens every control is at least 44 px tall.
  7. 1.1.1ASCII art is decoration and hidden from screen readers; photos carry real alt text.
  8. 2.3.3With reduced motion on, entrances render in their final state and page changes are plain navigation.
  9. 2.2.2Anything that moves on its own can be paused. Status dots blink six times, then hold still.
  10. 1.4.13Tooltips stay while you hover them and close on Esc.
  11. no-jsWithout JavaScript every page is still readable. With it, no content waits on an animation.

your settings, as this page sees them

motion
everything renders in its final state without JavaScript
pointer
—
shortcuts
—

Something doesn't work for you, with a screen reader, a switch, a zoom level? Tell me and I'll fix it.

[07]> fc-list | grep Mono

One monospace, the whole way down: it is the voice of a terminal, and it keeps every column of ASCII honest.

family
JetBrains Mono Variable, by JetBrains (OFL-1.1)
axis
weight 100–800, one file for every weight
file
latin subset, 39.5 KB woff2, preloaded on every page
fallback
Consolas at 109.1% · Menlo at 99.7%, metric-matched so the swap doesn’t move a line
ligatures
off: code reads as typed
display
none added: the page headers are the same mono, up to 216 px, tracked tight

The type scale, the colours and every shared component, with their states, are laid out on the styleguide.

[08]> cat CREDITS

Built on other people's good work.

globe
Land outlines from Natural Earth (public domain), via world-atlas
portrait
A photo of me, reduced to a 232 × 226 tone map and drawn as glyphs
art
The hourglass, the nautilus, this keyswitch and the 404 compass started as renders from FLUX1.1 [pro] via fal.ai. They only ever appear as glyphs. No people.
code
The open-source packages under Stack, with their licenses
built by
Me, Wesli Miggels, much of it through coding agents (Claude Code) that I direct and check

[09]> git rev-list --count HEAD

The site's version number is its commit count. It sits in the nav, the footer and here, and it changes with every commit.

0034

evolutions

built 05 OCT 2026

This page is part of evolution 0034. Every change before it is in the log, numbered, with the day it happened.

EVOLUTION 0034