Publish your groove
In the first post we built a drum machine in the browser with nothing to install — the Strudel engine arrives at runtime when you press play. Those demos were deliberately forgetful: reload the page, lose the beat.
Music wants a memory. This post adds one, and then makes it public. The demo below is the same step sequencer, plus a name field, a publish button, and a shared gallery. Make a beat, and load the grooves other readers of this post have published:
The memory is one hook
Here is the entire database layer:
jsconst { database, useLiveQuery } = useFireproof("groovebook");
// save
await database.put({ type: "groove", name, grid, authorHandle, timestamp });
// the gallery — re-renders itself whenever anyone publishes
const { docs: grooves } = useLiveQuery("type", { key: "groove" });
No server to set up, no schema migration, no fetch calls. The database is
local-first: it lives on your device, so a save lands instantly and works
offline, and it syncs in the background to everyone with access. That's why
the gallery in the embed above updates live — useLiveQuery is a question
the app keeps asking, and the answer changes under it when someone on the
other side of the world hits publish.
Get posts like this in your inbox
One email field. Real updates. No algorithm required.
Two honest details about who sees what:
- Signed out, your saves stay on this device. You can jam and save all day anonymously — it's your browser's copy. Sign in and your publishes join the shared book.
- Publishing is instant and social. Each groove is stamped with its
author's handle, and the gallery renders it with their avatar — that's the
ViewerTagcomponent doing identity for free.
The bouncer is ten lines
Who gets to write into a shared database? Every vibe ships a server-side access rule — a little function that sees every write. Here's this app's whole policy:
jsexport default function (doc, oldDoc, user, ctx) {
if (doc.type === "groove") {
if (!user) throw { forbidden: "sign in to publish grooves" };
const authorOk =
doc.authorHandle === user.userHandle ||
(user.isOwner && oldDoc && doc.authorHandle === oldDoc.authorHandle);
if (!authorOk) {
throw { forbidden: "sign your groove with your own handle" };
}
if (oldDoc && oldDoc.authorHandle !== user.userHandle && !user.isOwner) {
throw { forbidden: "not your groove" };
}
return { channels: ["grooves"], grant: { public: ["grooves"] } };
}
throw { forbidden: "unknown document type" };
}
Read it like house rules: grooves go on the public shelf (anyone can read them, even signed out — that's how the embed shows you the gallery), every groove must be signed with its real author's handle, and after that only the author — plus the app's owner, who keeps a moderation override — can touch it — and even the owner's moderation power can't reassign a groove to a different author. The first check stops a rude client from publishing under someone else's name (or under no name, which would make the doc anyone's to edit), the second stops anyone else from rewriting it. The rule runs on the server, so it holds no matter what the client sends.
From public jam to private band
That one file is the dial between "public jam session" and "private collaboration":
- Public by default. A normal deploy is world-readable — perfect for a gallery like this, where the point is that strangers hear each other's beats.
- Private when you want a band, not an audience. Deploy with
--privateand the app is members-only; people you invite can read and write, and everyone else gets the door. The access rule above still governs each document — membership decides who's in the room, your rule decides what they can touch in it. - In between. Because the rule is just code, "collaborators can edit anything, viewers can only read" or "submissions welcome, publishing is mine" are each a couple of lines, not a backend project.
The part that stays constant: your app's code never changes between these postures. Same sequencer, same hook, same gallery — you're only deciding who holds a copy.
Remix it
The groovebook is public — open it, remix it, and you get the sequencer, the database, and the access rule as your starting point. Swap the drum rows for chords, or the gallery for a setlist. And if you want the full live-coding version with an editor and an AI prompt box, that's jchris/strudel.
Your band is a remix away
Take the groovebook and make it yours — public jam or private session.
Start building →