A calm, tactile bubble-wrap fidget — tap soft bubbles on a sheet, hear a gentle pop, and get a fresh sheet when you're done. Part of the Unwind mini-game series, inspired by ORelax.
Play: https://bubble-wrap.shwethaadiraj.workers.dev
Best on a phone browser (Chrome on Android works great). Open the link above — no account, install, or setup needed.
- Tap or click any bubble to pop it. You'll see the dome flatten and hear a soft pop.
- Keep popping until the sheet is empty — a fresh sheet appears automatically (layout varies a little each time).
- A brief toast shows this sheet's pops and your lifetime total (saved on your device).
- Use the speaker button (top right) to mute or unmute. Your choice is remembered.
There are no timers, scores, levels, or failure states. Just keep popping to your heart's content.
| Layer | Choice |
|---|---|
| Package manager | pnpm |
| Build | Vite 8 + React 19 + TypeScript |
| Styling | Tailwind CSS 4 (UI chrome only) |
| Rendering | Canvas 2D |
| Audio | @foleyjs/core — synthesized pop cue |
git clone <repo-url>
cd bubble-wrap
pnpm install
pnpm devOpen http://127.0.0.1:5173.
| Command | Description |
|---|---|
pnpm dev |
Start dev server with HMR |
pnpm build |
Type-check and build for production |
pnpm preview |
Serve the production build locally |
pnpm lint |
Run Oxlint |
Set the public site URL for Open Graph, Twitter, canonical, sitemap, and robots (no trailing slash; https:// is added if omitted):
cp .env.example .env.production
# edit VITE_SITE_URL when deployingSEO copy and OG dimensions live in src/lib/seo.ts. The Vite build injects them into index.html and writes robots.txt + sitemap.xml into dist/.
| Key | Purpose |
|---|---|
bubble-wrap-muted |
Mute preference |
bubble-wrap-total-pops |
Lifetime pop count across sheets |
src/
games/BubbleWrap/ # canvas engine, sheet grid, React shell
hooks/ # pop hook, haptics stub
lib/ # constants, audio state, Foley, pop stats
docs/
intent/ # confirmed product intent
public/ # favicon, OG image, manifest
Private — personal project.