DEV Community

Arthur031221
Arthur031221

Posted on

Sending trip places as a stamp sheet, with the whole story in the URL fragment

When I get back from a trip I send friends a list of places. They read it in one glance and the order I had in mind is gone. So I built a small page that turns up to six places and one line each into a sheet of postage stamps, and a link that opens them one tap at a time.

A friend taps through a link and the stamps land one by one

The story is the link

There is no backend. The story is a JSON array, base64url encoded, in the URL fragment:

#v1=WzEsWyJKaWFveGkiLCJIb3Qgc3ByaW5ncyBpbiByYWluIl0s...
Enter fullscreen mode Exit fullscreen mode

Browsers do not send the fragment to a server, so the host never sees the text. Six stamps fit in a link of 313 to 323 characters on the project's GitHub Pages address, and the longest story the limits allow is 1079.

The part I spent the most time on is decoding. A link can be pasted into a chat and cut, or edited by hand. The decoder checks every field against the same cleaning function the editor uses, then encodes the result again and compares it with the original link. If they differ in any byte, including padding or a version written as v01, the page shows a damaged link message instead of a half story.

One renderer for three jobs

The preview in the editor, the reveal and the saved PNG all call the same function on a 1080 by 1350 canvas. The reveal passes a progress value so the newest stamp scales and fades in. Because the function only needs a 2D context, I test it in Node with a recording stand in, and I ship it as one ES module.

import { drawSheet, encodeStory } from './renderer.js'

const stamps = [{ place: 'Jiaoxi', note: 'Hot springs in rain' }]
drawSheet(canvas.getContext('2d'), stamps)
Enter fullscreen mode Exit fullscreen mode

Each stamp is drawn from its place name. A hash of the name seeds a few sine terms that bend nested rings into contour lines, so the same name always draws the same stamp and nothing is clip art.

Fitting text on a stamp

Place names run from two Chinese characters to ten Latin letters. A small line breaker puts breaks between CJK characters, keeps closing punctuation off the start of a line, balances two lines, and shrinks the type before it splits a word. It takes a measuring function, so it is unit tested with a fake ruler.

What is tested, and what is not

There are 47 unit tests and 32 browser tests that run on Chromium and Firefox in CI. They cover typing, reordering, the reveal order, damaged links, text that looks like markup, and the saved PNG. I have not tested Safari, in-app browsers or real phones.

Try it: https://arthur031221.github.io/WishYouWereHere/
Source: https://github.com/Arthur031221/WishYouWereHere

Top comments (0)