Initial commit: Pay2Play! satirical music player
Paywalled music player where every feature costs money — pause, resume, skip, volume, even closing the app. Built with React 18 and Tone.js via CDN, zero build step. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
96b1ecf7e6
6 changed files with 851 additions and 0 deletions
55
CLAUDE.md
Normal file
55
CLAUDE.md
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## What this is
|
||||
|
||||
Pay2Play! is a satirical music player where every interaction is paywalled. It's part of [donothireus.com](https://donothireus.com). The humor comes from absurd micro-charges for basic actions (pause, resume, volume, skip, etc.).
|
||||
|
||||
## Development
|
||||
|
||||
There is no build step, no package manager, and no dependencies to install. A static file server is required (Babel standalone fetches `app.js` via XHR).
|
||||
|
||||
**Run locally:**
|
||||
```bash
|
||||
cd public/ && python3 -m http.server 8080
|
||||
```
|
||||
|
||||
Any static file server works (Caddy, nginx, etc.).
|
||||
|
||||
## Architecture
|
||||
|
||||
The app is split across three files in `public/`:
|
||||
- `index.html` — HTML shell with CDN script tags and meta/OG tags
|
||||
- `style.css` — animations, range input styling, hover/focus/active states
|
||||
- `app.js` — all React components and logic (JSX compiled in-browser by Babel standalone)
|
||||
|
||||
**CDN dependencies (no local install):**
|
||||
- React 18 + ReactDOM (production UMD builds)
|
||||
- Tone.js 14.8 (procedural synth audio)
|
||||
- Babel standalone 7.26 (in-browser JSX → JS)
|
||||
|
||||
**Key data structures:**
|
||||
- `SONGS[]` — track definitions. Each song is either procedural (has `gen`, `wave`, `bpm` fields) or MP3-based (has `url` field). The two modes use different playback engines.
|
||||
- `SUBS{}` — defines every paywalled feature (`pause`, `resume`, `skip`, `volume`, `lyrics`, etc.) with subscription price, one-time micro-transaction price, and description.
|
||||
- `LYRICS_DATA[][]` — word arrays per song, shown redacted (every 3rd word visible) unless the user pays for the lyrics subscription.
|
||||
- `AD_COPY[]` — rotating fake ad strings shown during the unskippable exit countdown.
|
||||
|
||||
**Audio playback has two modes:**
|
||||
1. **Procedural (default):** Tone.js synth loops driven by each song's `gen()` callback. No audio files needed.
|
||||
2. **MP3:** When a song has a `url` property, HTML5 `Audio` is used instead. Songs can mix modes.
|
||||
|
||||
**Core component:** `PayPlay` is the single top-level React component containing all state and logic. Sub-components are `WelcomeBanner` (onboarding overlay), `Modal` (3-phase paywall: choose/insufficient/success), `ExitAd` (unskippable countdown with rotating ad copy), and `CBtn` (icon button with aria-label).
|
||||
|
||||
**Paywall flow:** User clicks a feature → `tryAct(key, fn)` checks if subscribed → if not, opens `Modal` → user chooses subscribe (full price) or one-time micro-payment → balance is deducted with cha-ching sound → success celebration → action executes. If insufficient funds, the modal stays open with a top-up button. The 3-second free listening limit is enforced via a `useEffect` watching `prog`, with audio degradation at 2s and a visual countdown badge.
|
||||
|
||||
**Theme toggling:** The "Dark Mode" button is a joke — paying for it toggles the app to a painfully bright light mode. All colors are derived from `bright` state via local variables (`bg`, `card`, `text`, `muted`, `accent`), so the theme affects the entire player.
|
||||
|
||||
## Deployment
|
||||
|
||||
Serve the `public/` directory. See README.md for Caddy config examples, including subpath mounting at `/payplay`.
|
||||
|
||||
## Adding songs
|
||||
|
||||
- **Procedural:** Add entry to `SONGS[]` with `gen(synth, time)` callback, `wave` type, `bpm`, `duration`, and `color`.
|
||||
- **MP3:** Put file in `public/audio/`, add entry with `url: "/audio/filename.mp3"` and `duration` (in seconds). Use `ffprobe` to get duration.
|
||||
Loading…
Add table
Add a link
Reference in a new issue