# 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.