Patinate
Patinate renders Strava rides over an OpenStreetMap basemap as an SVG plate. Each ride is one low-alpha stroke; where rides overlap, the strokes add up to brighter colour, so the streets ridden often come forward like patina on handled metal. The plate prints as a poster or embeds as a page hero.

One Rust binary carries the whole path, so a pipeline job can run it with nothing else installed.
syncpulls activities through Strava OAuth into a SQLite cache. The watermark advances only after every upsert lands, so a partial run resumes.fetch-osmgeocodes a city through Nominatim and pulls the basemap from Overpass, over https only.- The clipped activity is the only type the renderer accepts.
--anonymizedrops rider, bike, year and type attributes from the SVG and cuts its description to city and country;--webis the embed preset.- Themes are JSON.
noir_heat,blueprint_heat,warm_beige,cycle_heat,newsprintandverdigrisship inside the binary;--themes-diradds or overrides by name. - Configuration is TOML plus
PATINATE_*environment variables, so a pipeline sets it without a file.
Clipping at an exact circle around home does not hide it: every cut end lies on that circle, and a circle fit over the render recovers the centre to within metres.
- The zone’s centre is drawn once from a secret salt, uniform over a disk of the configured offset around home. Its radius is the privacy radius plus the offset plus a metre, so home stays strictly inside.
- Each cut end is trimmed a further distance along the track, seeded by salt, activity and crossing, so the ends scatter in a band.
- All randomness is SHA-256 over the salt: repeated renders are identical and cannot be averaged against each other. A positive radius with no salt refuses to run.
- Tracks still converge toward the gap. The zone hides the address, not the neighbourhood.
Single rider only. Cities across the antimeridian are not handled, and the basemap renders in full with no viewport culling. MIT licensed.