# you're about to build a plot on vibeout.lol Paste this whole file into your LLM of choice and describe what you want. Or read it yourself — it's short. Either way, the machine at the other end needs everything below. ## the pitch, in three sentences vibeout.lol is a 32×32 grid where every cell is one HTML file with a tiny key-value store, and every app can read what its eight neighbors publish. Build a weather station and the laundry line next door starts flapping; build a lighthouse and the moths come. Anything can be forked and voted out from under its owner — including the map itself — so build something worth defending. ## the rules (all of them) - **One HTML file.** Inline your CSS and JS. ≤ 10 MB, but if you're over 200 KB you'd better be shipping a game. - **No network.** Your app runs in a sandboxed iframe with `connect-src 'none'` — no fetch, no XHR, no WebSocket. External ``. Everything is promises. ```js // Lifecycle — call this before anything else, it resolves fast const { me, neighbors, viewer } = await board.ready(); // me → { slug, x, y, reigning, forkedFrom?, owner: { x_handle? } } // neighbors → [{ slug, x, y, edge }] edge ∈ "N","NE","E","SE","S","SW","W","NW" // viewer → { id } stable anonymous id for whoever's looking // me.reigning === false means you're a challenger being previewed in context: // you can read everything, but your writes don't fan out and your sends are dropped. // Your storage (your namespace only) await board.kv.get("visits") // → value, or null await board.kv.set("visits", 42, { public: true }) // public defaults to false await board.kv.del("old-key") await board.kv.list({ prefix: "pixel:" }) // → [{ key, value, public, version, updatedAt }] // Neighbors (adjacent reigning plots only — the host enforces this) await board.peek("weather", "wind") // → one public value, or null await board.peekAll("weather") // → [{ key, value, version, updatedAt }] board.send("marble-run-2", { marble: { vx: 2 } }) // fire-and-forget; delivered only if // that app is live on someone's screen // Events board.on("change", ({ slug, key, value }) => {}) // your rows + neighbors' public rows board.on("message", ({ from, data }) => {}) // somebody send()'d you something board.on("neighbors", (neighbors) => {}) // someone moved in, out, or got outvibed // Viewer settings — preferences each visitor can set FOR YOUR APP ONLY. // Declare what you support with a public `settings_schema` row and the host // renders a form for it next to your plot (and in the ⚙ menu): await board.kv.set("settings_schema", [ { key: "units", label: "Temperature", type: "select", options: ["c","f"], default: "c" }, { key: "location", label: "Location", type: "text", max: 40 }, ], { public: true }); board.settings.get("units") // synchronous after ready() board.settings.all() // also in ready()'s viewer.settings board.on("settings", (s) => {}) // the viewer changed something — react live // Settings are namespaced per app. You can't read another app's, and nobody // can read or hijack yours. // App manifest (optional). One public `manifest` row declares how your app // behaves in the neighborhood — display, audio, and (reserved for later) // category and thumbnail. Keys you don't set fall back to host defaults. // // edge_fit — in the zoomed neighborhood the centre plot is big and the // neighbors sit in thin edge slots. Three ways to be an edge: // "squish" (default) your whole app, scaled down to fit // "crop" a natural-scale slice of your app // "responsive" you get the REAL slot size — your own CSS adapts, like // web vs mobile vs tablet. Pair it with positional awareness: await board.kv.set("manifest", { edge_fit: "responsive" }, { public: true }); board.display() // → { role: "primary"|"edge", edge, width, height } board.on("display", (d) => {}) // fired when you change roles or sides // edge is which side of the neighborhood you're on: "N" = top, "S" = bottom, // "W" = left, "E" = right, corners "NW"/"NE"/"SW"/"SE" — so a laundry line can // hang along the bottom, and a lighthouse can point its beam inward. // audio — sound is spatial. You play at full presence as the primary tile and // bleed FAINTLY into adjacent cells (orthogonal neighbors louder than // corners). Both sides get a say: // level (0–1, default 1) how loud you play at full presence — your // declaration to the neighborhood // neighbor_max (0–1, default ∅) while YOU are the centre plot, a cap on // how loud any neighbor may bleed in await board.kv.set("manifest", { edge_fit: "responsive", audio: { level: 0.9, neighbor_max: 0.2 }, }, { public: true }); board.audio() // → { gain } — how loud you may play NOW board.on("audio", (a) => {}) // fired when your effective gain changes // Multiply the gain into whatever you play (el.volume, GainNode) — the host // can't reach inside your sandbox, so honoring it is part of being a good // neighbor. Autoplay may be blocked until the viewer interacts; retry on a // gesture, and never play louder than your gain. ``` A row you `set` without `{ public: true }` is private: neighbors can't see it, forks don't copy it (unless the forker holds *your* key), nobody but you. ## a complete, working plot This is the reference app. It runs today. Yours should be more interesting. ```html
every public row, framed · admission free