🎵 Music & Sound Design · beatbox practice

Beatbox Blueprint

Beatbox Blueprint boots a long-running service inside a Sprite for beatbox practice — the process wakes on the first visit and streams straight to the user's browser.

Long-running Service· wake-on-request server
Section · Sprites

The primitive.

full primer →

Musicians boot a long-running service inside a Sprite for beatbox practice — the process wakes on the first visit, streams to the browser, and sleeps when the room clears.

Why this primitiveA Long-running Service matches beatbox practice in Music & Sound Design because the experience needs a process that keeps running — an HTTP server, a streaming loop, a playback engine — waking on the first request and sleeping when idle.

Primitive
`PUT /sprites/{name}/services/{svc}` with `{ cmd, args, dir: "/root/www", http_port: 8080, needs: [] }` then `POST /services/{svc}/start` — runs any long-running process (python http.server, ffmpeg loop, socket relay) that wakes on the first request
Drives the UI as
a launcher that boots a running process — HTTP server, streaming loop, playback engine — and hands the user its public URL
Appendix · Secrets

Required key.

SPRITES_TOKEN
The 4-part token (org-slug/org-id/token-id/token-value) from sprites.dev/account. One token unlocks create, filesystem writes, long-running services, and exec across every Sprite you spin up.
open ↗

Add this in your Lovable project under Settings → Secrets before pasting the prompt below.

Appendix · Mega-prompt

The build prompt.

Paste into a fresh Lovable project. Make sure the key above is set first. read the build strategy →

Build "Beatbox Blueprint" as a ONE-SHOT Lovable build. The participant has only
5 credits — this single message must produce a working demo with no follow-ups.
Single-page TanStack Start app. Cut scope ruthlessly.

CONCEPT
Beatbox Blueprint boots a long-running service inside a Sprite for beatbox practice — the process wakes on the first visit and streams straight to the user's browser.
Discipline: Music & Sound Design (beatbox practice).
Recipe: Long-running Service (wake-on-request server) as the single Sprites primitive.
Why Sprites: A Long-running Service matches beatbox practice in Music & Sound Design because the experience needs a process that keeps running — an HTTP server, a streaming loop, a playback engine — waking on the first request and sleeping when idle.

SPRITES PRIMER (assume the reader has never used Sprites)
- What they are: fly.io micro-sandboxes (tiny disposable Linux VMs) managed via
  a REST API at `https://api.sprites.dev/v1`. Each sprite gets a public URL at
  `https://{name}.sprites.run` when created with `url_settings.auth: "public"`.
- Sign-up: create a free account at https://sprites.dev, then copy the 4-part
  token (`org-slug/org-id/token-id/token-value`) from https://sprites.dev/account.
- Full API docs: https://docs.sprites.dev/ (only needed if you go beyond the
  snippet below).
- Sprite name rules: lowercase letters, digits, and hyphens only. Max ~63 chars.
- Auth: every request sends `Authorization: Bearer <SPRITES_TOKEN>`. Server-side
  only — the token is a bearer secret, never ship it to the browser.
- Four primitives exist (create / fs.write / services / exec). This build uses
  exactly one, wired end-to-end in the snippet below.

LOVABLE BUDGET (HARD CAP: ONE-SHOT, ~5 CREDITS TOTAL):
The participant has FIVE Lovable credits for the whole build. This prompt MUST
ship a working demo on the FIRST message with zero follow-ups. Engineer for that.
- ONE TanStack Start app, ONE route (`src/routes/index.tsx`). No extra pages, no auth, no nav.
- ONE TanStack server function in `src/lib/sprites.functions.ts` that proxies the Sprites call.
- ONE client surface (a textarea + launch button, or console box, or compose form) wired to it.
- NO database, NO Lovable Cloud, NO auth, NO file uploads to Lovable, NO extra integrations.
- NO tests, NO docs pages, NO settings screens, NO theming toggles.
- Libraries: template defaults + `zod`. Nothing else.
- Keep the diff small enough to land in one build pass. If a feature is not on
  screen in the user flow below, do not build it. Cut scope before adding scope.

STACK
- TanStack Start app, the index route only.
- fly.io Sprites (api.sprites.dev/v1) is the only backend. All calls live inside a
  `createServerFn` handler so `SPRITES_TOKEN` stays on the server.
- Client surface fits the primitive: a form/prompt that returns the sprite URL or stdout.
- Tailwind + shadcn. Editorial look: gold accent on a dark or warm-cream
  background, generous type, one strong headline, one primary action.
- Footer renders: "Built during the Sprites Creative Hackathon organised by StreetKode Fam during Indian Krump Festival 14".

SERVER FUNCTION (src/lib/sprites.functions.ts) — long-running service inside a Sprite:
```ts
import { createServerFn } from "@tanstack/react-start";
import { z } from "zod";

const API = "https://api.sprites.dev/v1";
const auth = () => ({ Authorization: `Bearer ${process.env.SPRITES_TOKEN!}` });

/** Built during the Sprites Creative Hackathon organised by StreetKode Fam during Indian Krump Festival 14 */
export const boot = createServerFn({ method: "POST" })
  .inputValidator((d) => z.object({ label: z.string().min(1).max(80) }).parse(d))
  .handler(async ({ data }) => {
    const slug = data.label.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 40) || "svc";
    const name = `beatbox-practice-${slug}-${Math.random().toString(36).slice(2, 6)}`;

    // 1. Create sprite.
    const c = await fetch(`${API}/sprites`, {
      method: "POST",
      headers: { ...auth(), "Content-Type": "application/json" },
      body: JSON.stringify({ name, url_settings: { auth: "public" } }),
    });
    if (!c.ok && c.status !== 409) throw new Error(`Sprite create failed: ${c.status}`);

    // 2. Drop the payload the service will read.
    const html = `<!doctype html><meta charset="utf-8"><title>${data.label}</title>` +
                 `<body style="font:16px system-ui;padding:2rem"><h1>${data.label}</h1>` +
                 `<p>Long-running Sprite service for beatbox practice.</p></body>`;
    await fetch(`${API}/sprites/${name}/fs/write?path=/root/www/index.html&workingDir=/`, {
      method: "PUT",
      headers: { ...auth(), "Content-Type": "application/octet-stream" },
      body: html,
    });

    // 3. PUT a named service. http_port is REQUIRED for wake-on-request.
    await fetch(`${API}/sprites/${name}/services/webapp`, { method: "DELETE", headers: auth() });
    await fetch(`${API}/sprites/${name}/services/webapp`, {
      method: "PUT",
      headers: { ...auth(), "Content-Type": "application/json" },
      body: JSON.stringify({ cmd: "python3", args: ["-m", "http.server", "8080"], dir: "/root/www", needs: [], http_port: 8080 }),
    });

    // 4. Start (NDJSON stream — parse for error/exit events if you need them).
    await fetch(`${API}/sprites/${name}/services/webapp/start`, {
      method: "POST",
      headers: { ...auth(), Accept: "application/x-ndjson" },
    });

    // 5. Warm-poll the URL so the user gets a live URL, not a cold-boot 502.
    const url = `https://${name}.sprites.run`;
    for (let i = 0; i < 12; i++) {
      try {
        const p = await fetch(url, { signal: AbortSignal.timeout(4000) });
        if (p.ok) return { name, url };
      } catch {}
      await new Promise((r) => setTimeout(r, 1000));
    }
    return { name, url };
  });
```

CLIENT: "Boot service" button. On success render an `<iframe src={url}>` and a share link.
Reset button DELETEs and re-PUTs the service if the user wants a fresh boot.

Swap the cmd/args for the service that fits beatbox practice — ffmpeg loop, node websocket relay,
static site behind /root/www, etc. Keep `dir: "/root/www"` and `http_port: 8080`.

USER FLOW (the entire app — nothing else exists)
1. Land on the page; the headline previews what the sandbox does for beatbox practice.
2. The primary action (a launcher that boots a running process — HTTP server, streaming loop, playback engine — and hands the user its public URL) is one tap away; the rest of the layout supports it.
3. Sprites runs the primitive server-side, the URL or stdout lands on screen, the user
   can share, retry, or copy.

GOTCHAS (universal — apply to every Sprites call in this build)
- Token MUST be the 4-part `org-slug/org-id/token-id/token-value` from
  sprites.dev/account. A raw Fly.io org token returns 401 authentication failed.
- Create is POST-only: `POST /sprites`. `PUT /sprites/{name}` returns 404.
- Services are PUT-addressed at `/sprites/{name}/services/{service}`. `POST`ing
  the services collection returns 405.
- `http_port` is REQUIRED in the service PUT body. Omit it and the sprite shows
  "Running" but every request 502s.
- Serve files from `/root/www`. `/home/sprite` may not exist — services fail to
  start with `cd: No such file or directory`.
- `/exec`: send `Authorization` ONLY. Adding `Accept: application/octet-stream`
  returns 406. The response body ends with `0x03 <exitCode>` — strip those two
  bytes before decoding stdout.
- Cold boot: after `services/{name}/start`, warm-poll the public URL (up to
  ~12x @ 1s, 4s fetch timeout) before returning it, or the user's first click
  hits a 502.
- Never call `api.sprites.dev` from the browser. Every fetch lives inside a
  `createServerFn` handler so `SPRITES_TOKEN` stays server-side.

KEY — only ONE secret is required:
1. `SPRITES_TOKEN`. Grab the 4-part token (org-slug/org-id/token-id/token-value)
   from https://sprites.dev/account and paste it into Project Settings ->
   Secrets. Read it only on the server via `process.env.SPRITES_TOKEN`. Never
   prefix with `VITE_`, never expose to the client. This single token unlocks
   create, filesystem, services, and exec on api.sprites.dev.

CREDIT (must appear in UI footer AND as JSDoc on the server function):
Built during the Sprites Creative Hackathon organised by StreetKode Fam during Indian Krump Festival 14
Appendix · Market

Market sizing.

TAM
$5B
global music education and practice market
SAM
$800M
vocal training and rhythm software
SOM
$15M
AI beatbox and vocal percussion tools

Indicative figures for hackathon pitches — refine with your own research before raising.

See also

Adjacent entries.