No updates detected yet. GitHub releases will appear here.
Loading the full README. The saved excerpt is shown below.
A travel itinerary drawn as its own duration. Every hour of a trip is one
circle, dark through the night and pale through the day, with a coloured bar
marking the stretches where you actually stopped somewhere rather than kept
moving. Underneath, each day repeats on a 24 hour ring.
Nothing is compressed to fit. The band runs at a fixed 24 pixels per hour,
so distance across the page is literally time: an eight day trip is about
4,700 pixels wide, which is why the canvas scrolls sideways.Read the full README ↗A travel itinerary drawn as its own duration. Every hour of a trip is one circle, dark through the night and pale through the day, with a coloured bar marking the stretches where you actually stopped somewhere rather than kept moving. Underneath, each day repeats on a 24 hour ring.
Nothing is compressed to fit. The band runs at a fixed 24 pixels per hour, so distance across the page is literally time: an eight day trip is about 4,700 pixels wide, which is why the canvas scrolls sideways.
This started as a Figma plugin. The page here is that plugin rebuilt to run in a browser, so anyone can try it without installing anything.
Live: https://beginyanarthur.github.io/travel-timeline-visualization/
index.html the published page (built, do not edit by hand)
web/index.html the page source
build.py turns web/index.html into index.html
og.png social preview card, 1200x630
og-card.html the source of og.png
src/ the Figma plugin itself
samples/ two real trips in the plugin's JSON shape
web/index.html is also published as a Claude Artifact, where the host
supplies the <head>. GitHub Pages does not, so build.py adds it, along
with the Open Graph tags and a real file download. The Artifact sandbox blocks
page-initiated downloads, so that button can only copy to the clipboard there.
Edit web/index.html, never index.html, then:
python3 build.py
Set PAGES_URL at the top of build.py before the first deploy. Social
previews need an absolute URL, so a relative one will not resolve.
The feedback form emails each answer to Arthur through Formspree. It lives
in one dialog, opened from a button in the header, from a short prompt that
appears once after someone's first drawing, and from the invitation at the
foot of the page. On a phone it opens as a bottom sheet. Once someone has
sent feedback, the invitation at the foot goes away in that browser and the
workbench fills the window; Cookie settings is also in the ⋯ menu for that
reason. The endpoint is set near the bottom of web/index.html:
var FORM_ENDPOINT = 'https://formspree.io/f/xzebnlql';
The id names a form, not an address, so it is safe in a public page and no email address appears in the source.
What arrives in the inbox:
A hidden trap field catches bots, since the endpoint is public. It is off screen, out of the tab order and hidden from screen readers, so no person can fill it by accident and lose their message.
If a send fails, the answer is copied to the clipboard and the note says so,
so nothing anyone writes is lost. Empty FORM_ENDPOINT to go back to copying
only.
Formspree's free plan allows 50 submissions a month. The Claude Artifact copy of the page cannot send at all, because its sandbox blocks outside requests, so there it always falls back to copying.
Visits, recordings and heatmaps go to Microsoft Clarity. build.py adds the
tag on Pages only, from CLARITY_ID; leave that empty and no script ships.
Events follow a visit from arriving to writing back. Filter recordings
by any of them, for example generated, to watch only the people who drew.
| Stage | Events |
|---|---|
| Arriving | trip-restored |
| Building a trip | sample-loaded, leg-added, leg-removed, hotel-added, hotel-removed, card-reordered, trip-imported, trip-pasted, import-failed, trip-cleared |
| Drawing | generated (first drawing of a visit), generated-again, generate-blocked |
| Exploring | zoomed-in, zoomed-out, zoomed-by-gesture, actual-size, fit-width, full-screen, panned |
| Taking it away | printed, print-blocked (pressed before anything was drawn), json-exported |
| Cookies | cookies-allowed, cookies-declined |
| Feedback | feedback-prompt-shown, feedback-prompt-dismissed, feedback-opened-header, feedback-opened-prompt, feedback-opened-footer, feedback-sent, feedback-failed |
panned and zoomed-by-gesture are sent once a visit, since a drag or a
pinch fires dozens of times a second.
Tags describe the trip without naming it. Every drawing sets legs,
hotels, trip_days, transport (for example flight,train) and
trip_source (typed, sample, imported, pasted or restored). A
blocked press sets blocked_by: dates, no-legs, leg-duration or
hotel-dates, never the message itself, because that can quote a leg's
own label.
This is the beginning of the README. Read the rest at the original. Read the full README ↗
What people type never reaches Clarity. The dates, legs, hotels, the
drawing, the print pages, the status line, the feedback form and the paste
box carry data-clarity-mask="True", so recordings show clicks and scrolls,
not destinations.