# Festival 4D — Build Instructions **Audience:** Claude Opus 4.8 executing autonomously in this repo (`/Users/m3ultra/Documents/festifun`). **Goal:** Turn multiple fan-shot smartphone videos of the same concert into a synchronized, explorable 4D experience: time-aligned multi-video playback, 3D scene reconstruction with camera poses, a free-roam "god's eye" viewer, AR-style overlays projected onto each video, and AI-tagged moments on a shared timeline. This is an entertainment/replay project (same family as sports "bullet time" replay). Keep all naming, prompts, and UI copy in the concert domain. --- > **Parallel execution:** this spec is the canonical reference; the work is organized into lanes for multiple concurrent agents — see [`plan/README.md`](plan/README.md) for the phase map, branch protocol, file ownership, and your lane brief. If you were given a lane brief, it overrides the serial ordering note below (your lane's milestones still apply verbatim). ## 0. Ground rules - **Build milestones in dependency order.** Each milestone has acceptance criteria; do not start the next until the current one passes. When executing serially (single agent), go M0→M9; when executing in lanes, follow `plan/`. - **Work without real footage.** The user may not have multi-camera concert videos yet. Every milestone must be verifiable with the synthetic fixtures described in M0. When real footage arrives, the same pipeline runs on it unchanged. - **Prefer boring tech.** SQLite (not PostgreSQL — this is a local, single-user, offline-first tool; do not add a DB server dependency). Vanilla JS + Three.js via Vite (no React). FastAPI + uvicorn. Python 3.11+, managed with `uv` if available, else venv + pip. - **COLMAP is an external binary.** Detect it at runtime (`shutil.which("colmap")`); if missing, print install instructions (`brew install colmap` on macOS) and — critically — the whole app must still run using the synthetic-poses fixture. Never make COLMAP a hard import-time dependency. - **Commit per milestone** with a message describing what now works. Initialize git first (`git init`). ## 1. Repository layout ``` festifun/ ├── README.md # setup + usage, written last (M9) ├── pyproject.toml # backend deps: fastapi, uvicorn, numpy, scipy, librosa, │ # soundfile, sqlalchemy, pydantic, opencv-python-headless, │ # google-genai, anthropic, openai (last three = classifier providers) ├── backend/ │ ├── festival4d/ │ │ ├── __init__.py │ │ ├── cli.py # `python -m festival4d `: ingest | sync | reconstruct | events | serve │ │ ├── config.py # paths, constants │ │ ├── db.py # SQLAlchemy engine/session, schema │ │ ├── ingest.py # probe videos (ffprobe), extract audio, register in DB │ │ ├── audio_sync.py # GCC-PHAT pairwise offsets + global solve │ │ ├── frames.py # sharpness-aware frame sampling for SfM │ │ ├── sfm.py # COLMAP orchestration, model export, normalization, interpolation │ │ ├── geometry.py # quaternion slerp, ray casting, triangulation, coord conversions │ │ ├── events_ai.py # audio candidate detection + Claude classification │ │ ├── api.py # FastAPI app │ │ └── synthetic.py # test-fixture generator (see M0) │ └── tests/ # pytest; every numeric module gets a test ├── frontend/ │ ├── index.html │ ├── package.json # vite + three │ └── src/ │ ├── main.js │ ├── state.js # global store: manifest, playhead, playing, selected camera │ ├── transport.js # master clock + per-video sync (M4) │ ├── videoGrid.js #