Vibes DIY
Vibes DIY / Blog
From the build log

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:

strudel-groovebook — open the live vibe

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 ViewerTag component 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 --private and 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 →

Enjoyed this? Get the next post by email

One email field. Real updates. No algorithm required.

Prefer a feed? RSS · Atom