Pixel-art mindmap. 100% static site, zero dependencies, zero build step.
ES modules don't work over file://, so you need a small server:
python -m http.server 8000
# then open http://localhost:8000- Push the repo to GitHub.
Settings → Pages → Build and deployment → Source: Deploy from a branch.- Branch
main, folder/ (root). The site is served as-is.
For a custom domain: Settings → Pages → Custom domain, and a CNAME file
(containing your domain) appears at the repo root — replace it or delete it
if you fork the project to host it under a different domain.
The app is available in French and English. It guesses the language from
the browser on first visit; your choice in Settings → Language is then
remembered and always takes priority. Adding a new language is a small,
self-contained change in js/i18n.js (one dictionary object + one list
entry) — see the comments there.
- Right-click: radial menu (create rectangle/sign/circle/hexagon/liaison, color, text, delete, export/import).
- Sign: a larger rectangle with a wood texture, for titles/panels.
- Deleting an object makes it explode into pieces (animation, synced with clients).
- Wheel: zoom · drag the background: pan the view.
- Drag a rectangle: elastic movement.
- Drag a circle/hexagon's edge: resize · drag its inside: move it.
- Double-click: edit the text (Escape to confirm). On an image rectangle: opens the image full-size.
- Drag-and-drop an image (or Ctrl-V an image from the clipboard): onto a rectangle to put it inside, onto empty space to create an image rectangle ("Remove image" to take it out). The image is always shown in full; the rectangle keeps a roughly constant size.
- Link (radial menu on a rectangle): attaches a URL; an ↗ badge appears and a click opens the link in a new tab.
- Delete: erases the selected element.
- Ctrl-C / Ctrl-V: copy-paste the selected element (pasted at the mouse position).
- A rectangle whose center is inside a circle/hexagon takes its color.
A hexagon (e.g. "Today") aggregates links to rectangles stored elsewhere. Drag a rectangle (from a circle) into a hexagon: a link (dashed border) is created, the original goes back to its place. The link keeps the source circle's color and mirrors its text/image; renaming or deleting the source updates (or removes) the link. Links can be freely placed inside the hexagon.
To copy your board from one device to another (e.g. desktop → phone):
- On the source device (HOST): radial menu → "+ Liaison". A QR code block appears.
- On the other device (CLIENT): scan the QR (or open the link — clicking the block copies it).
- Once connected, the client's board is replaced by the host's, then both stay synced live, in both directions, as long as the host's window stays open.
Synced: the content (text, image, color, description, links, creations/deletions) and the objects' positions — but the latter only on drop (not during the drag), and the other screen animates it. The camera stays independent: each screen keeps its own zoom/framing (e.g. one screen zoomed out, another zoomed into a circle). If the same element is edited simultaneously, the host wins.
Encrypted P2P connection (WebRTC via PeerJS); only connection identifiers go through the signaling broker, the content travels directly between the two browsers. The PeerJS / QR libs are loaded on demand (CDN), the app stays dependency-free at rest. Images and voice memos are stored locally (IndexedDB) and only transit once per peer, never through the broker.
Permanent host (optional): to keep sync available even with all browsers closed, you can run a small Node server on a Raspberry Pi that acts as a permanent host — see server/. The app doesn't need any modification: devices connect to it via ?peer=<pi-id>.
The liaison id is stable (remembered): refreshing the host's page and recreating the liaison gives back the same link/QR. On a network drop, the host automatically reconnects to the broker (same id) and clients retry the connection — no need to rescan. If the link leaks, "New link" (liaison block menu) regenerates an id: the old URL becomes invalid, the board is preserved.
Privacy-wise: content travels over encrypted WebRTC (DTLS), directly between peers in the normal case; if a direct connection isn't possible, it's relayed (encrypted) by PeerJS's TURN servers. The broker only ever sees connection identifiers.
- Interaction locked by default (so a block isn't accidentally moved): only panning (1 finger) and zooming (pinch) work. Long-press then only offers "Enable". Once enabled, the standard radial menu comes back (with "Lock" to re-lock it).
- 1 finger: drags the background (pan) or, once enabled, moves an element.
- 2 fingers: pinch to zoom.
- Long-press: radial menu · double-tap: edit / view the image (interaction enabled).
Automatic save in the browser (localStorage). JSON export/import via the radial menu.
Adding ?file=<url> to the address loads that JSON instead of localStorage, without overwriting your personal board:
https://your-instance.example/?file=https://example.com/board.json
The file must be accessible over CORS (same origin, raw.githubusercontent.com, gist…). A relative path also works: ?file=boards/demo.json.