From 3dddeda40f07e466cb0df5ad306968e553e72854 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 14 Dec 2025 19:11:59 +0000 Subject: [PATCH 1/2] Initial plan From e9cebd4030616ab4cc6728102ab7f5319015df6a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 14 Dec 2025 19:24:12 +0000 Subject: [PATCH 2/2] docs: revamp documentation to professional product-level quality Co-authored-by: polsala <15796004+polsala@users.noreply.github.com> --- README.md | 388 +++++++++++++++++++++---- docs/ARCHITECTURE.md | 619 ++++++++++++++++++++++++++++++++++++++++ docs/CONTRIBUTING.md | 391 +++++++++++++++++++++++++ docs/README.md | 203 +++++++++++++ docs/SCREENSHOTS.md | 294 +++++++++++++++++++ docs/TROUBLESHOOTING.md | 539 ++++++++++++++++++++++++++++++++++ docs/studio.md | 394 +++++++++++++++++++++++-- docs/visualizer.md | 401 +++++++++++++++++++++++++- 8 files changed, 3128 insertions(+), 101 deletions(-) create mode 100644 docs/ARCHITECTURE.md create mode 100644 docs/CONTRIBUTING.md create mode 100644 docs/README.md create mode 100644 docs/SCREENSHOTS.md create mode 100644 docs/TROUBLESHOOTING.md diff --git a/README.md b/README.md index 31d3960..241af0e 100644 --- a/README.md +++ b/README.md @@ -1,76 +1,344 @@ -# BitAndPlay +
-BitAndPlay is a browser-only 8/16-bit chiptune workstation: deterministic seeded generation, a Studio arrangement view, Tone.js transport, a rhythm-reactive 3D visualizer, and dual export paths (live capture + HQ offline WAV). The repo is ready for GitHub Pages deployment out of the box. +# ๐ŸŽต BitAndPlay -## Quick start +### Browser-Based Chiptune Workstation & Visualizer + +*Create, arrange, and visualize 8/16-bit music with deterministic generation, professional DAW tools, and stunning 3D graphicsโ€”all in your browser.* + +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) +[![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue)](https://www.typescriptlang.org/) +[![React](https://img.shields.io/badge/React-19-61dafb)](https://reactjs.org/) +[![Vite](https://img.shields.io/badge/Vite-7-646cff)](https://vitejs.dev/) + +[โœจ Live Demo](https://polsala.github.io/BitAndPlay/) | [๐Ÿ“– Documentation](docs/) | [๐Ÿ› Report Bug](https://github.com/polsala/BitAndPlay/issues) | [๐Ÿ’ก Request Feature](https://github.com/polsala/BitAndPlay/issues) + +
+ +--- + +## ๐ŸŽฏ Overview + +**BitAndPlay** is a complete chiptune music production environment that runs entirely in your browserโ€”no installation, no server, no dependencies. Powered by cutting-edge web technologies, it combines: + +- **๐ŸŽผ Deterministic Music Generation** โ€“ Create reproducible, seed-based chiptune compositions +- **๐ŸŽš๏ธ Professional Studio Mode** โ€“ Full-featured DAW with multi-track arrangement, piano roll, and step sequencer +- **๐ŸŽฎ Retro Sound Engine** โ€“ Authentic 8/16-bit synthesis via Tone.js (pulse, triangle, saw, noise, PCM) +- **๐ŸŒŒ Reactive 3D Visualizer** โ€“ Five stunning WebGL scenes that dance to your music +- **๐Ÿ’พ High-Quality Export** โ€“ Capture live or render offline WAV with perfect fidelity +- **๐Ÿ”— Shareable Sessions** โ€“ Export/import projects or share via URL parameters + +Whether you're a musician, developer, or chiptune enthusiast, BitAndPlay delivers a polished, performant experience with the aesthetic and workflow of professional DAWsโ€”entirely client-side. + +--- + +## โœจ Key Features + +### ๐ŸŽฒ Seeded Generation (Playground Mode) +- **Reproducible Compositions**: Same seed always produces the same song +- **Smart Presets**: Eight genre-inspired starting points (Arcade, Dance, Chill, etc.) +- **Musical Control**: Adjust key, scale, energy, density, syncopation, and complexity +- **Instant Variations**: Reroll arrangements while keeping the core vibe +- **Theory-Aware**: Generates chord progressions, melodies, bass lines, and drums using music theory + +### ๐ŸŽ›๏ธ Studio Mode (Arrangement View) +- **Multi-Track Editor**: Add/remove tracks with different instruments (Pulse, Triangle, Saw, Sine, Noise, PCM) +- **Clip-Based Workflow**: Drag, resize, and arrange clips on a timeline with snap-to-grid +- **Piano Roll & Step Sequencer**: Edit melodic tracks with a piano roll; drum tracks with a step grid +- **Mute/Solo & Mixer**: Per-track volume and routing controls +- **Loop Region**: Set loop points for seamless playback +- **Apply-on-Next-Bar**: Click-free updates synchronized to the beat + +๐Ÿ“š [Studio Documentation โ†’](docs/studio.md) + +### ๐ŸŽจ 3D Visualizer +Five distinct, rhythm-reactive scenes built with Three.js and React Three Fiber: + +1. **Tunnel Spectrum**: Cascading torus rings that pulse and rotate +2. **Neon Grid**: Retro grid horizon with height-driven waves +3. **Orbit Bars**: Orbiting bars with inner glow particles +4. **Black Hole**: Swirling particle vortex with dynamic perspective +5. **Interactive Bubbles**: Grabbable, throwable spheres you can fling around + +- **Quality Settings**: Low/Medium/High for different hardware +- **Accessibility**: Respects `prefers-reduced-motion` +- **Cinema Mode**: Fullscreen visualizer with in-canvas controls + +๐Ÿ“š [Visualizer Documentation โ†’](docs/visualizer.md) + +### ๐ŸŽน Audio Engine +- **Tone.js Transport**: Professional scheduling, swing, and quantization +- **Authentic Synthesis**: Multiple oscillator types + noise channels for classic chip sounds +- **Master Processing**: Limiter and analyzer for consistent levels and visualization +- **Click-Free Updates**: Bar-aligned scheduling prevents audio glitches + +### ๐Ÿ’พ Export & Sharing +- **Fast Capture**: Real-time export via MediaRecorder (WebM/Ogg) +- **HQ Offline Render**: Deterministic WAV encoding via OfflineAudioContext +- **Project Files**: Save/load JSON projects with full session state +- **URL Sharing**: Share compositions via query parameters (`?seed=...&preset=...`) +- **Local Persistence**: Optional localStorage auto-save + +--- + +## ๐Ÿš€ Quick Start + +### Prerequisites +- Node.js 18+ and npm (or any package manager) +- Modern browser with Web Audio API support (Chrome, Firefox, Edge, Safari) + +### Installation ```bash +# Clone the repository +git clone https://github.com/polsala/BitAndPlay.git +cd BitAndPlay + +# Install dependencies npm ci + +# Start development server npm run dev ``` -Open the provided URL and click **Enable audio** to start the audio context (required by browsers). +Open your browser to the provided URL (usually `http://localhost:5173`) and click **Enable Audio** to initialize the audio context. + +### Available Scripts + +| Command | Description | +|---------|-------------| +| `npm run dev` | Start Vite development server with hot reload | +| `npm run build` | Type-check and build production bundle | +| `npm run preview` | Preview production build locally | +| `npm run lint` | Run ESLint on the codebase | +| `npm run format` | Format code with Prettier | +| `npm run test` | Run Vitest unit tests | + +--- + +## ๐Ÿ“– Usage Guide + +### Getting Started + +1. **Enable Audio**: Click the overlay buttonโ€”browsers require a user gesture to start audio +2. **Choose Your Mode**: + - **Playground**: Generate songs with sliders and presets + - **Studio**: Arrange tracks manually like a DAW +3. **Adjust Settings**: Use the right panel tabs (Generate/Mix/Visualizer/Effects/Export) +4. **Play & Export**: Hit play and export when ready (fast or HQ WAV) + +### Playground Workflow + +1. Select a **Preset** (e.g., "Arcade Hero", "Synth Dance") +2. Adjust **Musical Settings**: + - Key & Scale (C major, A minor, etc.) + - Energy, Density, Syncopation, Complexity sliders +3. Click **Generate** to create a new song +4. Use **Variation** to reroll while keeping the core structure +5. Fine-tune with **BPM** and **Swing** controls +6. Switch to **Studio** mode to manually edit the result + +### Studio Workflow + +1. Click **Add Track** and choose an instrument +2. **Draw Clips**: Click-drag in the timeline to create new clips +3. **Edit Patterns**: + - Select a clip to open the editor drawer + - Melodic tracks: piano roll with transpose/quantize + - Drum tracks: step grid with kick/snare/hat/perc/fx lanes +4. **Arrange**: Move and resize clips to build your song structure +5. **Mix**: Mute/solo tracks; adjust global BPM/swing +6. **Loop & Playback**: Set loop region and enable "Apply on next bar" for smooth edits + +### Visualizer + +- Switch scenes in the **Visualizer** tab +- Adjust **Quality** based on your GPU (Low for integrated, High for dedicated) +- Enable **Cinema Mode** to hide panels and maximize the visualizer +- The visualizer responds to audio frequencies: lows, mids, highs, and overall energy -### Scripts -- `npm run dev` โ€“ Vite dev server -- `npm run build` โ€“ typecheck + production build -- `npm run lint` โ€“ ESLint -- `npm run test` โ€“ Vitest unit tests -- `npm run preview` โ€“ preview the production build locally +--- -## Features -- **Seeded generator**: reproducible songs with presets, key/scale, energy/density/syncopation/complexity sliders, and variation reroll. -- **Studio (arrangement)**: tracks you can add/remove, clip lanes with snap/zoom, drag/resize clips, piano roll & drum step editors, loop region, playhead follow. Apply-on-next-bar keeps updates click-free. See `docs/studio.md`. -- **Tone.js engine**: per-track synths (pulse, triangle, saw, sine, noise, PCM), limiter + analyser, swing/quantize, bar-aligned updates to avoid clicks. -- **Transport**: play/pause/stop, BPM + swing controls, regenerate/variation, โ€œapply on next barโ€ safety, cinema toggle (with in-canvas exit button). -- **3D visualizer**: five scenes (Tunnel Spectrum, Neon Grid, Orbit Bars, Black Hole, Interactive Bubbles) built with @react-three/fiber + Three.js; low/mid/high band mapping and quality selector. See `docs/visualizer.md`. -- **Export**: fast capture via `MediaRecorder` + HQ offline WAV render with OfflineAudioContext + WAV encoder; JSON import/export; shareable URL query params. -- **Persistence**: optional localStorage save/restore; share links (`?seed=...&preset=...`). -- **UI**: minimal dark Ableton-style skin with shadcn/ui building blocks and Tailwind utilities. +## ๐Ÿ—๏ธ Architecture -## Architecture +BitAndPlay follows a modular, type-safe architecture: ``` src/ - app/ App shell, layout, overlay wiring - audio/ - engine.ts Tone.js transport, master bus, scheduling - instruments.ts Track synth definitions + master chain - generator/ Deterministic song builder (rng/theory/patterns) - render/ Offline render + WAV encoder helpers - store/ Zustand global store (transport/UI/export/studio state) - ui/ Top bar, transport, right panel tabs, shadcn components - viz/ Visualizer canvas + scenes + analyser hook - studio/ Arrangement view components and defaults - types/ Song and project types +โ”œโ”€โ”€ app/ # Application shell, layout, overlays +โ”œโ”€โ”€ audio/ +โ”‚ โ”œโ”€โ”€ engine.ts # Tone.js transport, master bus, scheduling +โ”‚ โ”œโ”€โ”€ instruments.ts # Synth definitions per track type +โ”‚ โ”œโ”€โ”€ generator/ # Seeded song generation (RNG, theory, patterns) +โ”‚ โ””โ”€โ”€ render/ # Export logic (MediaRecorder, offline WAV) +โ”œโ”€โ”€ store/ # Zustand global state (transport, UI, projects) +โ”œโ”€โ”€ ui/ +โ”‚ โ”œโ”€โ”€ components/ # shadcn/ui-style primitives +โ”‚ โ””โ”€โ”€ tabs/ # Right panel tab content +โ”œโ”€โ”€ viz/ +โ”‚ โ”œโ”€โ”€ VisualizerCanvas.tsx # Three.js canvas setup +โ”‚ โ”œโ”€โ”€ scenes/ # Five visualizer scenes +โ”‚ โ””โ”€โ”€ useAudioBands.ts # Audio analysis hook +โ”œโ”€โ”€ studio/ # Studio mode components (timeline, clips, editors) +โ””โ”€โ”€ types/ # TypeScript definitions (Song, Project, Track, Clip) ``` -- **Audio engine**: `engine.ts` initializes Tone, master limiter, analyser, and media stream for fast export. Track parts are scheduled with `Tone.Part`, looping per song length; updates optionally defer to the next bar for click-free changes. -- **Generator**: `generate.ts` builds chord progressions, melody/bass arps, and drum grids on a 16-step/bar lattice. Mulberry32 RNG (`rng.ts`) ensures repeatable output from a seed. -- **Visualizer**: `useAudioBands` samples the analyser without React re-renders; scenes animate via `useFrame` in r3f at 60fps where possible. Quality toggles change instance counts / DPR. -- **Export**: fast export uses `MediaRecorder` on the master media stream; HQ export uses `OfflineAudioContext` with simple oscillators/noise and encodes 16-bit PCM WAV via `audioBufferToWav`. - -## Deployment (GitHub Pages) -1) Push to `main` with Actions enabled. -2) In repo settings โ†’ Pages, choose โ€œGitHub Actionsโ€ as the source. -3) The workflow `.github/workflows/deploy.yml` builds with `GITHUB_PAGES=true` (sets Vite `base` to `/BitAndPlay/`) and publishes `dist` via `actions/deploy-pages`. - -## Troubleshooting -- **No sound**: click the โ€œEnable audioโ€ overlay; browsers block AudioContext until a user gesture. -- **Choppy visuals**: switch visualizer quality to Medium/Low; `prefers-reduced-motion` is respected by reduced animation intensities. -- **Fast export missing**: some browsers lack `MediaRecorder` for Web Audio; use HQ WAV instead. -- **Clicks on regenerate**: keep โ€œApply on next barโ€ enabled to reschedule pattern updates. - -## Known limitations -- MediaRecorder MIME support depends on the browser (`audio/webm` preferred, falls back to `audio/ogg`). -- Offline render uses simplified oscillators/noise (not the live Tone graph) to guarantee deterministic WAV output in all browsers. -- No backend; large localStorage saves can be cleared by the browser. - -## Performance tips -- Lower visualizer quality on integrated GPUs. -- Keep swing modest (<15%) to maintain tight drums. -- Shorter songs (8โ€“16 bars) render and export faster. - -## Testing -- Deterministic RNG and generator coverage lives in `src/audio/generator/*.test.ts`. -Run `npm run test` to execute Vitest in node environment. +### Key Technologies + +- **Frontend**: React 19 + TypeScript (strict mode) +- **Build Tool**: Vite 7 +- **Audio**: Tone.js 15 + Web Audio API +- **3D Graphics**: Three.js + React Three Fiber +- **State Management**: Zustand +- **UI Components**: Radix UI primitives + Tailwind CSS +- **Testing**: Vitest + +๐Ÿ“š [Architecture Deep Dive โ†’](docs/ARCHITECTURE.md) + +--- + +## ๐Ÿค Contributing + +We welcome contributions from the community! Whether you're fixing bugs, adding features, or improving documentation, your help is appreciated. + +### How to Contribute + +1. **Fork** the repository +2. **Clone** your fork: `git clone https://github.com/YOUR_USERNAME/BitAndPlay.git` +3. **Create a branch**: `git checkout -b feature/your-feature-name` +4. **Make your changes** and write tests if applicable +5. **Test**: Run `npm run test` and `npm run lint` +6. **Commit**: Follow [Conventional Commits](https://www.conventionalcommits.org/) +7. **Push** and open a **Pull Request** + +### Development Guidelines + +- Maintain strict TypeScript types +- Write concise, non-obvious comments only +- Keep components small and reusable +- Respect the existing code style (Prettier + ESLint) +- Add unit tests for new generator logic or utilities +- Update documentation for user-facing changes + +๐Ÿ“š [Contributing Guide โ†’](docs/CONTRIBUTING.md) + +--- + +## ๐Ÿšข Deployment + +### GitHub Pages (Recommended) + +BitAndPlay is designed for zero-config deployment to GitHub Pages: + +1. **Enable GitHub Actions** in your repository settings +2. **Push to `main`** branch +3. **Configure Pages**: Repo Settings โ†’ Pages โ†’ Source: "GitHub Actions" +4. The workflow `.github/workflows/deploy.yml` automatically builds and deploys + +The build process sets `GITHUB_PAGES=true`, which configures Vite's `base` path to `/BitAndPlay/`. + +### Other Platforms + +BitAndPlay is a static site and can be deployed anywhere: + +```bash +npm run build +# Upload the `dist/` folder to your hosting provider +``` + +Supports: Vercel, Netlify, Cloudflare Pages, AWS S3, etc. + +--- + +## ๐Ÿ› Troubleshooting + +### No Sound + +**Cause**: Browsers block AudioContext until user interaction. +**Solution**: Click the "Enable Audio" overlay button. + +### Choppy Visuals + +**Cause**: Integrated GPU or heavy scene at high quality. +**Solution**: Switch visualizer quality to Medium or Low in the Visualizer tab. + +### Fast Export Missing + +**Cause**: Some browsers don't support MediaRecorder for Web Audio. +**Solution**: Use "HQ WAV" export instead (slower but always works). + +### Clicks or Pops on Regenerate + +**Cause**: Audio updates mid-bar can cause glitches. +**Solution**: Enable "Apply on next bar" in settings (default: on). + +๐Ÿ“š [Full Troubleshooting Guide โ†’](docs/TROUBLESHOOTING.md) + +--- + +## ๐Ÿ“ Known Limitations + +- **MediaRecorder MIME**: Browser-dependent; prefers `audio/webm`, falls back to `audio/ogg` +- **Offline Render Accuracy**: Uses simplified oscillators (not the live Tone.js graph) for deterministic output +- **No Backend**: All data is client-side; large localStorage saves may be cleared by the browser +- **PCM Sample Playback**: Limited to basic waveforms; no custom sample upload yet + +--- + +## โšก Performance Tips + +- **Lower Visualizer Quality**: Use Low/Medium on integrated GPUs +- **Modest Swing**: Keep swing below 15% for tight timing +- **Shorter Songs**: 8โ€“16 bars render and export faster +- **Disable Cinema Mode**: When editing, panels provide better workflow + +--- + +## ๐Ÿงช Testing + +BitAndPlay includes unit tests for core logic: + +```bash +npm run test # Run all tests +npm run test:watch # Watch mode +npm run test:ui # Vitest UI +``` + +**Coverage**: Deterministic RNG, music theory, pattern generation +**Location**: `src/audio/generator/*.test.ts` + +--- + +## ๐Ÿ“œ License + +BitAndPlay is open source software licensed under the [MIT License](LICENSE). + +Copyright (c) 2025 PSala + +--- + +## ๐Ÿ™ Acknowledgments + +- **Tone.js** โ€“ Powerful Web Audio framework +- **Three.js** & **React Three Fiber** โ€“ 3D graphics engine +- **Radix UI** โ€“ Accessible UI primitives +- **shadcn/ui** โ€“ Design system inspiration +- **Tailwind CSS** โ€“ Utility-first styling + +--- + +## ๐Ÿ“ฌ Contact & Support + +- **Issues**: [GitHub Issues](https://github.com/polsala/BitAndPlay/issues) +- **Discussions**: [GitHub Discussions](https://github.com/polsala/BitAndPlay/discussions) +- **Pull Requests**: Always welcome! + +--- + +
+ +**Built with โค๏ธ by the BitAndPlay community** + +[โญ Star us on GitHub](https://github.com/polsala/BitAndPlay) | [๐Ÿด Fork](https://github.com/polsala/BitAndPlay/fork) | [๐Ÿ“– Docs](docs/) + +
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..c33cacd --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,619 @@ +# ๐Ÿ—๏ธ Architecture Deep Dive + +This document provides a comprehensive technical overview of BitAndPlay's architecture, design decisions, and implementation details. + +--- + +## ๐Ÿ“‹ Table of Contents + +- [High-Level Overview](#high-level-overview) +- [Technology Stack](#technology-stack) +- [Application Structure](#application-structure) +- [Audio Engine](#audio-engine) +- [Music Generation System](#music-generation-system) +- [Studio Mode Architecture](#studio-mode-architecture) +- [Visualizer System](#visualizer-system) +- [State Management](#state-management) +- [Export Pipeline](#export-pipeline) +- [Performance Considerations](#performance-considerations) +- [Design Patterns](#design-patterns) + +--- + +## ๐ŸŽฏ High-Level Overview + +BitAndPlay is a **client-side-only** web application with three major subsystems: + +1. **Audio Engine**: Tone.js-based music playback and synthesis +2. **Generator**: Deterministic, seed-based music composition +3. **Visualizer**: WebGL-based 3D audio-reactive graphics + +The application follows a **unidirectional data flow** pattern with Zustand for state management, and uses React's component model to organize UI and audio/visual systems. + +### Key Architectural Principles + +- **No backend**: All logic runs in the browser (Web Audio API, WebGL) +- **Deterministic generation**: Same seed always produces same song +- **Real-time performance**: Target 60 FPS for visualizer; low-latency audio +- **Type safety**: Strict TypeScript throughout +- **Modular design**: Clear separation between audio, UI, and visualization + +--- + +## ๐Ÿ› ๏ธ Technology Stack + +### Core Technologies + +| Layer | Technology | Purpose | +|-------|-----------|---------| +| **Framework** | React 19 | UI components and reactivity | +| **Language** | TypeScript 5.9 | Type safety and developer experience | +| **Build Tool** | Vite 7 | Fast dev server and optimized builds | +| **Audio** | Tone.js 15 | Web Audio abstraction and scheduling | +| **3D Graphics** | Three.js + R3F | WebGL rendering and scene management | +| **State** | Zustand 5 | Global state management | +| **UI Primitives** | Radix UI | Accessible, unstyled components | +| **Styling** | Tailwind CSS | Utility-first styling | +| **Testing** | Vitest 4 | Unit tests for logic and utilities | + +### Key Libraries + +- **@react-three/fiber**: React renderer for Three.js +- **@react-three/drei**: Useful Three.js helpers (minimal usage) +- **lucide-react**: Icon library +- **zod**: Runtime type validation (for export/import) +- **class-variance-authority**: Variant-based component styling + +--- + +## ๐Ÿ›๏ธ Application Structure + +``` +src/ +โ”œโ”€โ”€ app/ # Application shell +โ”‚ โ”œโ”€โ”€ App.tsx # Root component +โ”‚ โ”œโ”€โ”€ Layout.tsx # Main layout (top/bottom/right panels) +โ”‚ โ””โ”€โ”€ AudioEnableOverlay.tsx # Initial audio context gate +โ”‚ +โ”œโ”€โ”€ audio/ # Audio subsystem +โ”‚ โ”œโ”€โ”€ engine.ts # Tone.js transport, master bus +โ”‚ โ”œโ”€โ”€ instruments.ts # Per-track synth definitions +โ”‚ โ”œโ”€โ”€ generator/ # Music generation logic +โ”‚ โ”‚ โ”œโ”€โ”€ generate.ts # Main entry point +โ”‚ โ”‚ โ”œโ”€โ”€ rng.ts # Mulberry32 seeded RNG +โ”‚ โ”‚ โ”œโ”€โ”€ theory.ts # Scales, chords, progressions +โ”‚ โ”‚ โ”œโ”€โ”€ patterns.ts # Melody, bass, drum generation +โ”‚ โ”‚ โ””โ”€โ”€ *.test.ts # Unit tests +โ”‚ โ””โ”€โ”€ render/ # Export logic +โ”‚ โ”œโ”€โ”€ fastExport.ts # MediaRecorder capture +โ”‚ โ”œโ”€โ”€ offlineRender.ts # OfflineAudioContext WAV +โ”‚ โ””โ”€โ”€ audioBufferToWav.ts # WAV encoder +โ”‚ +โ”œโ”€โ”€ store/ # State management +โ”‚ โ””โ”€โ”€ useAppStore.ts # Zustand store (single source of truth) +โ”‚ +โ”œโ”€โ”€ ui/ # UI components +โ”‚ โ”œโ”€โ”€ components/ # shadcn/ui-style primitives +โ”‚ โ”‚ โ”œโ”€โ”€ Button.tsx +โ”‚ โ”‚ โ”œโ”€โ”€ Slider.tsx +โ”‚ โ”‚ โ”œโ”€โ”€ Select.tsx +โ”‚ โ”‚ โ””โ”€โ”€ ... +โ”‚ โ”œโ”€โ”€ tabs/ # Right panel content +โ”‚ โ”‚ โ”œโ”€โ”€ GenerateTab.tsx +โ”‚ โ”‚ โ”œโ”€โ”€ MixTab.tsx +โ”‚ โ”‚ โ”œโ”€โ”€ VisualizerTab.tsx +โ”‚ โ”‚ โ”œโ”€โ”€ EffectsTab.tsx +โ”‚ โ”‚ โ””โ”€โ”€ ExportTab.tsx +โ”‚ โ”œโ”€โ”€ TopBar.tsx +โ”‚ โ””โ”€โ”€ TransportBar.tsx +โ”‚ +โ”œโ”€โ”€ viz/ # Visualizer subsystem +โ”‚ โ”œโ”€โ”€ VisualizerCanvas.tsx # R3F canvas setup +โ”‚ โ”œโ”€โ”€ useAudioBands.ts # Audio analysis hook +โ”‚ โ””โ”€โ”€ scenes/ # Visualizer scenes +โ”‚ โ”œโ”€โ”€ TunnelSpectrum.tsx +โ”‚ โ”œโ”€โ”€ NeonGrid.tsx +โ”‚ โ”œโ”€โ”€ OrbitBars.tsx +โ”‚ โ”œโ”€โ”€ BlackHole.tsx +โ”‚ โ””โ”€โ”€ InteractiveBubbles.tsx +โ”‚ +โ”œโ”€โ”€ studio/ # Studio mode subsystem +โ”‚ โ”œโ”€โ”€ StudioView.tsx # Main studio container +โ”‚ โ”œโ”€โ”€ Timeline.tsx # Clip arrangement timeline +โ”‚ โ”œโ”€โ”€ ClipEditor.tsx # Piano roll / step sequencer +โ”‚ โ”œโ”€โ”€ TrackLane.tsx # Individual track lane +โ”‚ โ””โ”€โ”€ defaults.ts # Default clip/track settings +โ”‚ +โ”œโ”€โ”€ types/ # TypeScript definitions +โ”‚ โ”œโ”€โ”€ song.ts # Song, Track, Pattern types +โ”‚ โ””โ”€โ”€ project.ts # Project, Clip, UI state types +โ”‚ +โ””โ”€โ”€ utils/ # Shared utilities + โ”œโ”€โ”€ cn.ts # Tailwind class merger + โ””โ”€โ”€ ... +``` + +--- + +## ๐ŸŽน Audio Engine + +### Transport & Scheduling + +**File**: `src/audio/engine.ts` + +The audio engine is built on **Tone.js** and manages: + +- **Tone.Transport**: Global timing clock (BPM, swing, playback state) +- **Master Bus**: Limiter โ†’ Analyzer โ†’ Destination +- **Track Scheduling**: `Tone.Part` instances per track +- **MediaRecorder Stream**: For fast export + +**Initialization**: + +```typescript +export async function initAudio() { + await Tone.start(); + + // Master limiter prevents clipping + const limiter = new Tone.Limiter(-1).toDestination(); + + // Analyzer for visualizer + const analyser = new Tone.Analyser('fft', 2048); + limiter.connect(analyser); + + // MediaRecorder destination + const mediaStreamDest = Tone.context.createMediaStreamDestination(); + limiter.connect(mediaStreamDest); + + return { limiter, analyser, mediaStreamDest }; +} +``` + +**Monotonic Time Guards**: + +To prevent Tone's "start time must be greater than previous start time" error, the engine uses `Math.max(Tone.now(), lastScheduledTime)` when scheduling notes. + +### Instruments + +**File**: `src/audio/instruments.ts` + +Each track type maps to a Tone.js synth: + +| Instrument | Tone.js Synth | Waveform | Use Case | +|-----------|--------------|----------|----------| +| **Pulse** | `Tone.Synth` | `square` | Leads, arps | +| **Triangle** | `Tone.Synth` | `triangle` | Mellow melodies | +| **Saw** | `Tone.Synth` | `sawtooth` | Bright, buzzy tones | +| **Sine** | `Tone.Synth` | `sine` | Bass, sub-bass | +| **Noise** | `Tone.NoiseSynth` | `white` | Drums, FX | +| **PCM** | `Tone.Synth` | `pulse` | Flexible waveform | + +**Envelope Settings**: + +```typescript +{ + envelope: { + attack: 0.01, // Fast attack for chip sounds + decay: 0.1, + sustain: 0.5, + release: 0.1, + } +} +``` + +### Apply-on-Next-Bar + +**Problem**: Updating patterns mid-bar causes clicks. +**Solution**: Defer `loadSong` calls to the next bar boundary using `Tone.Transport.schedule`. + +```typescript +const nextBar = Math.ceil(Tone.Transport.seconds / barDuration) * barDuration; +Tone.Transport.schedule((time) => { + loadSongImmediate(song); +}, nextBar); +``` + +--- + +## ๐ŸŽฒ Music Generation System + +### Deterministic RNG + +**File**: `src/audio/generator/rng.ts` + +Uses **Mulberry32**, a simple, fast, seedable PRNG: + +```typescript +export function mulberry32(seed: number) { + return function() { + let t = seed += 0x6D2B79F5; + t = Math.imul(t ^ t >>> 15, t | 1); + t ^= t + Math.imul(t ^ t >>> 7, t | 61); + return ((t ^ t >>> 14) >>> 0) / 4294967296; + }; +} +``` + +**Properties**: +- Same seed โ†’ same sequence +- Fast (no crypto overhead) +- Good distribution for music generation + +### Music Theory + +**File**: `src/audio/generator/theory.ts` + +Defines scales, chords, and progressions: + +**Scales**: +```typescript +const SCALES = { + major: [0, 2, 4, 5, 7, 9, 11], + minor: [0, 2, 3, 5, 7, 8, 10], + pentatonic: [0, 2, 4, 7, 9], + // ... +}; +``` + +**Chord Progressions**: +```typescript +const PROGRESSIONS = { + 'I-V-vi-IV': [0, 4, 5, 3], // Pop progression + 'i-VII-VI-VII': [0, 6, 5, 6], // Minor progression + // ... +}; +``` + +**Chord Construction**: +- Triads: root, third (major/minor), fifth +- Extensions: 7th, 9th (optional) + +### Pattern Generation + +**File**: `src/audio/generator/patterns.ts` + +Generates melody, bass, and drum patterns: + +**Melody**: +1. Pick scale degrees from the current chord +2. Add passing tones with probability based on density +3. Apply syncopation via rhythmic displacement +4. Quantize to 16th-note grid + +**Bass**: +1. Use chord root as primary note +2. Add fifth or octave jumps +3. Simpler rhythm than melody (usually on beats 1 and 3) + +**Drums**: +1. Kick: Accent beats 1 and 3 (or all beats for dance) +2. Snare: Beats 2 and 4 +3. Hi-hat: 8th or 16th notes based on energy +4. Fills: Random variations based on complexity + +**Parameters**: +- **Energy**: Overall note density and rhythm intensity +- **Density**: Probability of adding notes +- **Syncopation**: Off-beat placement likelihood +- **Complexity**: Harmonic extensions and variations + +--- + +## ๐ŸŽ›๏ธ Studio Mode Architecture + +### Data Model + +**Track**: +```typescript +interface Track { + id: string; + name: string; + instrument: 'pulse' | 'triangle' | 'saw' | 'sine' | 'noise' | 'pcm'; + clips: Clip[]; + muted: boolean; + solo: boolean; +} +``` + +**Clip**: +```typescript +interface Clip { + id: string; + trackId: string; + startBar: number; + lengthBars: number; + pattern: Pattern; // Notes or drum hits +} +``` + +**Pattern**: +```typescript +interface Pattern { + notes: Note[]; // For melodic tracks + drums?: DrumHit[]; // For drum tracks +} + +interface Note { + pitch: number; // MIDI note number + time: number; // Position in bars + duration: number; +} +``` + +### Timeline Rendering + +**File**: `src/studio/Timeline.tsx` + +- **Horizontal scroll**: Canvas-style virtual scrolling +- **Snap-to-grid**: Calculated via `Math.round(x / gridSize) * gridSize` +- **Drag & drop**: Mouse events + state updates +- **Playhead**: Synced to `Tone.Transport.seconds` + +**Zoom**: +- Zoom level multiplies pixel-per-bar ratio +- Canvas re-renders on zoom change +- Scroll position preserved + +### Clip Editors + +**Piano Roll** (`src/studio/ClipEditor.tsx`): +- 2D grid: time (x-axis) ร— pitch (y-axis) +- Click to toggle notes +- Transpose: Shift all notes by N semitones +- Quantize: Snap notes to nearest grid line + +**Step Sequencer**: +- 2D grid: time (x-axis) ร— drum lane (y-axis) +- Click to toggle hits +- Fill: Add hit to every step +- Clear: Remove all hits + +--- + +## ๐ŸŒŒ Visualizer System + +### Audio Analysis + +**File**: `src/viz/useAudioBands.ts` + +Samples the Tone.js analyzer at 60 FPS without triggering React re-renders: + +```typescript +export function useAudioBands() { + const bandsRef = useRef({ low: 0, mid: 0, high: 0, energy: 0 }); + + useFrame(() => { + const fft = analyser.getValue(); // Float32Array of FFT bins + + bandsRef.current.low = averageBins(fft, 0, 10); // 20-250 Hz + bandsRef.current.mid = averageBins(fft, 10, 40); // 250-2000 Hz + bandsRef.current.high = averageBins(fft, 40, 100); // 2000+ Hz + bandsRef.current.energy = averageBins(fft, 0, 100); + }); + + return bandsRef.current; +} +``` + +**Why `useRef`?** +Updating state at 60 FPS would cause massive re-renders. `useRef` allows scenes to read values without triggering React. + +### Scene Architecture + +Each scene is a React Three Fiber component: + +```typescript +export function MyScene() { + const { low, mid, high, energy } = useAudioBands(); + const meshRef = useRef(); + + // Update every frame + useFrame(() => { + if (!meshRef.current) return; + + // Map audio to visuals + meshRef.current.scale.y = 1 + low * 2; + meshRef.current.rotation.y += mid * 0.01; + }); + + return ( + + + + + ); +} +``` + +### Instancing + +For performance, repeated geometry uses `THREE.InstancedMesh`: + +```typescript +const instancedMesh = new THREE.InstancedMesh(geometry, material, count); + +// Update each instance's transform +const matrix = new THREE.Matrix4(); +matrix.setPosition(x, y, z); +instancedMesh.setMatrixAt(index, matrix); +instancedMesh.instanceMatrix.needsUpdate = true; +``` + +--- + +## ๐Ÿ“ฆ State Management + +**File**: `src/store/useAppStore.ts` + +BitAndPlay uses a **single Zustand store** for all global state: + +```typescript +interface AppState { + // Transport + isPlaying: boolean; + bpm: number; + swing: number; + + // Generator + seed: number; + preset: string; + generatorParams: GeneratorParams; + + // Studio + tracks: Track[]; + clips: Clip[]; + selectedClipId: string | null; + + // UI + mode: 'playground' | 'studio'; + activeTab: string; + cinemaMode: boolean; + + // Visualizer + visualizerScene: string; + visualizerQuality: 'low' | 'medium' | 'high'; + + // Actions + play: () => void; + pause: () => void; + setBPM: (bpm: number) => void; + generateSong: () => void; + // ... +} +``` + +**Why Zustand?** +- Minimal boilerplate vs. Redux +- No provider wrapping needed +- TypeScript-friendly +- Fast updates (no context diffing) + +**Selectors**: + +```typescript +const bpm = useAppStore((state) => state.bpm); +const setBPM = useAppStore((state) => state.setBPM); +``` + +--- + +## ๐Ÿ’พ Export Pipeline + +### Fast Export (MediaRecorder) + +**File**: `src/audio/render/fastExport.ts` + +1. Connect Tone limiter to `MediaStreamDestination` +2. Create `MediaRecorder` with preferred MIME type (`audio/webm` or `audio/ogg`) +3. Start playback and recording simultaneously +4. Stop at song end; download the Blob + +**Pros**: Real-time (no waiting) +**Cons**: Browser-dependent codec; may not match live sound exactly + +### HQ Offline Render (WAV) + +**File**: `src/audio/render/offlineRender.ts` + +1. Create `OfflineAudioContext` with song duration +2. Recreate song with **simplified oscillators** (not Tone.js): + - Pulse โ†’ `OscillatorNode` with `square` waveform + - Triangle โ†’ `OscillatorNode` with `triangle` + - Noise โ†’ `AudioBufferSourceNode` with white noise buffer +3. Schedule all notes precisely +4. Render to `AudioBuffer` +5. Encode to WAV via `audioBufferToWav` + +**Why simplified oscillators?** +OfflineAudioContext doesn't support Tone.js graph consistently across browsers. Simplified oscillators guarantee deterministic output. + +**WAV Encoding**: + +```typescript +function audioBufferToWav(buffer: AudioBuffer): Blob { + const numChannels = buffer.numberOfChannels; + const sampleRate = buffer.sampleRate; + const length = buffer.length * numChannels * 2; // 16-bit + + const wavBuffer = new ArrayBuffer(44 + length); + const view = new DataView(wavBuffer); + + // Write WAV header (RIFF, fmt, data chunks) + writeString(view, 0, 'RIFF'); + view.setUint32(4, 36 + length, true); + writeString(view, 8, 'WAVE'); + // ... (full implementation in audioBufferToWav.ts) + + return new Blob([wavBuffer], { type: 'audio/wav' }); +} +``` + +--- + +## โšก Performance Considerations + +### Audio + +- **Scheduling ahead**: Notes scheduled 0.1s in advance to prevent glitches +- **Limiter**: Prevents clipping on loud passages +- **Memoized synths**: Synth instances reused per track + +### Visualizer + +- **Instancing**: 100+ objects with 1 draw call +- **Frustum culling**: Three.js removes off-screen objects +- **DPR scaling**: Quality settings adjust Device Pixel Ratio +- **Idle motion**: Simplified animations when audio is silent + +### React + +- **Memoization**: `useMemo` for expensive calculations +- **Refs over state**: For 60 FPS updates (visualizer, playhead) +- **Lazy loading**: Scenes loaded on-demand (potential future optimization) + +--- + +## ๐ŸŽจ Design Patterns + +### Separation of Concerns + +- **Audio logic** lives in `src/audio/` +- **UI logic** lives in `src/ui/` and `src/app/` +- **State** centralized in `src/store/` +- **Types** defined in `src/types/` + +### Composition over Inheritance + +- Small, reusable components +- Hooks extract shared logic +- No class components or inheritance chains + +### Immutability + +- Zustand updates use `immer` under the hood +- State is never mutated directly + +### Type Safety + +- Strict TypeScript throughout +- No `any` (use `unknown` and narrow with guards) +- Zod schemas for runtime validation (export/import) + +--- + +## ๐Ÿ”— Related Documentation + +- [Main README](../README.md) โ€“ Project overview +- [Studio Guide](studio.md) โ€“ Studio mode usage +- [Visualizer Guide](visualizer.md) โ€“ Visualizer scenes +- [Contributing](CONTRIBUTING.md) โ€“ How to contribute + +--- + +
+ +**Ready to dive deeper into the code?** + +[๐Ÿ  Back to Main Docs](../README.md) | [๐Ÿค Contributing Guide โ†’](CONTRIBUTING.md) + +
diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md new file mode 100644 index 0000000..f64479e --- /dev/null +++ b/docs/CONTRIBUTING.md @@ -0,0 +1,391 @@ +# ๐Ÿค Contributing to BitAndPlay + +Thank you for your interest in contributing to BitAndPlay! We welcome contributions from developers, designers, musicians, and documentation writers. This guide will help you get started. + +--- + +## ๐Ÿ“‹ Table of Contents + +- [Code of Conduct](#code-of-conduct) +- [How Can I Contribute?](#how-can-i-contribute) +- [Getting Started](#getting-started) +- [Development Workflow](#development-workflow) +- [Code Guidelines](#code-guidelines) +- [Testing](#testing) +- [Submitting Changes](#submitting-changes) +- [Community](#community) + +--- + +## ๐Ÿ“œ Code of Conduct + +We are committed to providing a welcoming and inclusive environment. By participating in this project, you agree to: + +- **Be respectful**: Treat all contributors with respect and kindness +- **Be constructive**: Provide helpful feedback and criticism +- **Be collaborative**: Work together to improve the project +- **Be patient**: Remember that everyone has different experience levels + +If you experience or witness unacceptable behavior, please report it via GitHub issues or contact the maintainers directly. + +--- + +## ๐ŸŽฏ How Can I Contribute? + +### ๐Ÿ› Reporting Bugs + +Found a bug? Help us fix it: + +1. **Search existing issues** to avoid duplicates +2. **Create a new issue** with: + - Clear, descriptive title + - Steps to reproduce the bug + - Expected vs. actual behavior + - Browser, OS, and version info + - Screenshots or console logs if applicable +3. **Use the bug report template** if available + +### ๐Ÿ’ก Suggesting Features + +Have an idea to improve BitAndPlay? + +1. **Check the roadmap** and existing issues first +2. **Open a feature request** with: + - Clear description of the feature + - Use cases and benefits + - Potential implementation approach + - Mockups or examples (if applicable) +3. **Discuss before implementing**: Large features should be discussed with maintainers first + +### ๐Ÿ“– Improving Documentation + +Documentation is crucial! You can help by: + +- Fixing typos or clarifying confusing sections +- Adding examples or tutorials +- Translating documentation to other languages +- Creating video tutorials or guides +- Capturing and adding screenshots + +### ๐ŸŽจ Contributing Code + +Ready to write code? See the sections below for detailed guidelines. + +--- + +## ๐Ÿš€ Getting Started + +### Prerequisites + +- **Node.js** 18+ (20+ recommended) +- **npm** 10+ +- **Git** for version control +- A modern browser (Chrome, Firefox, Edge, Safari) +- (Optional) A code editor with TypeScript support (VS Code recommended) + +### Fork & Clone + +1. **Fork** the repository on GitHub +2. **Clone your fork**: + ```bash + git clone https://github.com/YOUR_USERNAME/BitAndPlay.git + cd BitAndPlay + ``` +3. **Add upstream remote**: + ```bash + git remote add upstream https://github.com/polsala/BitAndPlay.git + ``` +4. **Install dependencies**: + ```bash + npm ci + ``` +5. **Start the dev server**: + ```bash + npm run dev + ``` + +### Project Structure + +``` +BitAndPlay/ +โ”œโ”€โ”€ src/ +โ”‚ โ”œโ”€โ”€ app/ # App shell and layout +โ”‚ โ”œโ”€โ”€ audio/ # Audio engine (Tone.js, generator, export) +โ”‚ โ”œโ”€โ”€ store/ # Zustand state management +โ”‚ โ”œโ”€โ”€ ui/ # UI components and tabs +โ”‚ โ”œโ”€โ”€ viz/ # 3D visualizer (scenes, canvas) +โ”‚ โ”œโ”€โ”€ studio/ # Studio mode (timeline, clips, editors) +โ”‚ โ””โ”€โ”€ types/ # TypeScript type definitions +โ”œโ”€โ”€ docs/ # Documentation +โ”œโ”€โ”€ public/ # Static assets (logo, favicon) +โ””โ”€โ”€ tests/ # Unit tests (Vitest) +``` + +For a deeper dive, see [ARCHITECTURE.md](ARCHITECTURE.md). + +--- + +## ๐Ÿ”„ Development Workflow + +### Branching Strategy + +1. **Create a feature branch** from `main`: + ```bash + git checkout -b feature/your-feature-name + ``` +2. **Use descriptive branch names**: + - `feature/add-new-visualizer-scene` + - `fix/audio-click-on-regenerate` + - `docs/improve-studio-guide` + - `refactor/simplify-generator-logic` + +### Making Changes + +1. **Keep changes focused**: One feature or fix per branch +2. **Write clean code**: Follow the [Code Guidelines](#code-guidelines) below +3. **Test your changes**: Run `npm run test` and `npm run lint` +4. **Document new features**: Update README.md or docs/ as needed +5. **Add unit tests**: For new generator logic or utilities + +### Syncing with Upstream + +Stay up-to-date with the main repository: + +```bash +git fetch upstream +git rebase upstream/main +``` + +Resolve any conflicts, then force-push to your fork: + +```bash +git push origin feature/your-feature-name --force +``` + +--- + +## ๐Ÿ“ Code Guidelines + +### TypeScript + +- **Strict mode**: Always use strict TypeScript (no `any` unless absolutely necessary) +- **Type everything**: Props, function parameters, return types +- **Use existing types**: Import from `src/types/` instead of redefining +- **No implicit `any`**: Enable `noImplicitAny` in tsconfig.json + +### React + +- **Functional components**: Use function components with hooks (no class components) +- **Keep components small**: Single responsibility; extract helpers to separate functions +- **Props destructuring**: Destructure props in function signatures +- **Use custom hooks**: Extract reusable logic to hooks (e.g., `useAudioBands`) + +### Styling + +- **Tailwind CSS**: Use utility classes for styling +- **No inline styles**: Unless dynamic (e.g., audio-driven animations) +- **Dark theme**: Maintain consistency with the existing dark palette +- **Responsive**: Ensure components work on mobile (though desktop-first is OK) + +### Code Style + +- **Prettier**: Format with `npm run format` before committing +- **ESLint**: Run `npm run lint` and fix all errors +- **Comments**: Only for non-obvious logic; prefer self-documenting code +- **Naming**: + - `camelCase` for variables and functions + - `PascalCase` for components and types + - `UPPER_SNAKE_CASE` for constants + +### Audio & Visualizer + +- **Tone.js best practices**: Always schedule events with Transport times +- **Monotonic time guards**: Prevent "start time must be greater" errors +- **Apply-on-next-bar**: Respect deferred updates when editing during playback +- **WebGL optimization**: Use instancing for repeated geometry; profile with DevTools + +--- + +## ๐Ÿงช Testing + +### Running Tests + +```bash +npm run test # Run all tests +npm run test:watch # Watch mode for development +npm run test:ui # Open Vitest UI in browser +``` + +### What to Test + +- **Generator logic**: Seeded RNG, music theory, pattern generation +- **Utilities**: Helper functions in `src/utils/` +- **Type safety**: Ensure types are correctly enforced +- **Edge cases**: Empty arrays, invalid inputs, boundary conditions + +### Writing Tests + +- **Use Vitest**: Tests live in `*.test.ts` files next to the source +- **Descriptive names**: `it('generates repeatable songs with the same seed')` +- **Arrange-Act-Assert**: Organize tests into setup, action, verification +- **Mock external dependencies**: Use Vitest mocks for Tone.js or browser APIs + +**Example**: + +```typescript +import { describe, it, expect } from 'vitest'; +import { mulberry32 } from './rng'; + +describe('mulberry32 RNG', () => { + it('generates repeatable sequences with the same seed', () => { + const rng1 = mulberry32(12345); + const rng2 = mulberry32(12345); + + expect(rng1()).toBe(rng2()); + expect(rng1()).toBe(rng2()); + }); + + it('generates different sequences with different seeds', () => { + const rng1 = mulberry32(12345); + const rng2 = mulberry32(54321); + + expect(rng1()).not.toBe(rng2()); + }); +}); +``` + +--- + +## ๐Ÿ“ค Submitting Changes + +### Before You Submit + +- [ ] **All tests pass**: `npm run test` +- [ ] **No lint errors**: `npm run lint` +- [ ] **Code is formatted**: `npm run format` +- [ ] **Build succeeds**: `npm run build` +- [ ] **Documentation updated**: If you changed user-facing behavior +- [ ] **Commit messages are clear**: See [Commit Guidelines](#commit-guidelines) + +### Commit Guidelines + +Follow [Conventional Commits](https://www.conventionalcommits.org/): + +``` +(): + + + +