a poster press · Strava in, prints out
Posters pulled from the roads you ride.
patinate draws your Strava activity onto a city basemap, one low-alpha stroke per ride. Where you ride often the strokes stack and the color deepens, the way patina builds on metal.

the prints
Six themes, one city.
The bundled Grand Rapids sample through every theme the binary ships. Open any of them on the press to change the inks.

Black ground, white road hierarchy, hot orange heat.

Deep blueprint blue, pale roads, amber heat.

Cream paper, sepia roads, terracotta heat.

Paper palette, steel-blue heat.

Letterpress black on gray stock, red heat.

Bronze roads on a dark green ground, verdigris heat.
Each print is patinate render --theme <name> --heat-bloom 1.8 --heat-alpha 2.5 on fixtures/, rendered at build time with just web.
the plates
Four layers, in register.
A render is layered the way a print is: a ground, then each plate in its own ink. Every plate answers to a flag.
basemap
Streets and water
OpenStreetMap roads in six tiers, water and parks. Leave it off with --heat-only.
glow
The bloom
Two wide, faint passes of the heat ink under the sharp one. Scale it with --heat-bloom; 0 is a hard line.
heat
The rides
One stroke per activity, cut at the privacy zone before it is drawn. Scale its opacity with --heat-alpha.
text
The colophon
City, count and scale bar. --web leaves it off so a page can set its own.
operating sequence
Four commands, in this order.
A single Rust binary. Strava and OpenStreetMap own the upstream data, your disk holds the caches, and render works only from what is on disk.
once
auth
patinate authOAuth with Strava, scope activity:read_all. The token refreshes itself after that.
writes tokens into config.toml, mode 0600
once per city
fetch-osm
patinate fetch-osm --city "Your City, USA" --radius-m 25000The OpenStreetMap basemap, from Overpass. Fetch again when you move or change the radius.
writes basemap.osm.json in the user cache dir
after rides
sync
patinate syncAn incremental pull of your activities. The only command you run often.
writes cache.db (SQLite) in the user data dir
whenever
render
patinate render --theme warm_beige --cycling --out heatmap.svgComposes the poster from what is on disk. No network.
writes heatmap.svg, or whatever --out names
after render
Three ways a render leaves the machine.
A print to hang
patinate render --theme noir_heat --anonymize --out poster.svg
rsvg-convert -w 3600 poster.svg -o poster.pngThe full poster: six road tiers, glow, heat and the text plate. Rasterize it for a print shop.
A page ground
patinate render --theme cycle_heat --cycling --web \
--transparent-bg --anonymize --out ground.svgNo text plate and no minor roads, over your own page color. cycle.dunn.dev is set on one.
An inline SVG
patinate render --theme cycle_heat --web --out inline.svgKeeps the data-type, data-year and data-bike attributes for page filters. Pass --anonymize before it goes public.
privacy
What the zone hides, and what it doesn't.
Nothing inside obfuscation_radius_m of home reaches the SVG. The hidden zone is offset from home in a direction and distance only your [privacy] salt knows, up to offset_m (750 m by default), and each cut end is trimmed a little further along the track.
It does not hide everything. Tracks still converge toward home, and a determined analyst can narrow it to roughly 100 to 250 m. A larger radius adds only modest cover. Treat it as a tool you check, not a promise.
For anything public, also pass --anonymize: it drops the data-* attributes and the <desc> that carries the center coordinates. Everything on this site is the bundled sample: seeded random walks over the Grand Rapids road graph, no real rider.