# Immediate waveform and real progress visualization ## Goal The application should no longer feel like a blank control surface after a file is selected. Loading audio is now the first visible step: the browser decodes the uploaded file locally and draws its waveform before the backend extraction job starts. During extraction, the waveform doubles as the progress surface. The completed portion of the pipeline is rendered over the left side of the uploaded waveform in the accent color; the remaining portion stays neutral. ## Implementation ### Browser-side uploaded waveform `web/app.js` builds an overview directly from the selected `File` using Web Audio: 1. read the file with `File.arrayBuffer()`; 2. decode with `AudioContext.decodeAudioData()`; 3. scan all decoded channels into a fixed-size peak envelope; 4. render that envelope immediately on `#waveform`; 5. keep the uploaded file as the `Source` transport target. No backend round-trip is needed for this first waveform. ### Real progress contract Backend job payloads now include a top-level `progress` object: ```json { "fraction": 0.1875, "completed_steps": 1, "total_steps": 8, "completed_units": 12.0, "total_units": 64.0, "stage_key": "stem", "stage_label": "Stem extraction / source load", "stage_fraction": 0.5, "stage_work_done": 4, "stage_work_total": 8, "basis": "exact completed work units: Demucs chunks when available, otherwise stage boundary units; no time-based estimates" } ``` The UI uses this object as-is. It does not animate guessed progress and does not estimate ETA. ### Demucs/stem progress `sample_extractor.extract_stem()` now accepts `progress_cb`. For separated-stem Demucs runs, the code computes the exact number of split chunks from model segment length, overlap, input length, and shift count. A small progress pool reports each completed chunk. For `stem=all` or cache hits, stem progress is reported as a completed single unit because no chunked separation work is performed. ### Stage progress `pipeline_runner.StageTiming` now includes: - `progress` - `work_done` - `work_total` Stages without internal chunk progress only update at exact boundaries: started and done. This is intentional. The app must show real state, not simulated activity. ### Waveform progress rendering `drawWaveform()` renders the same waveform in two colors during active jobs: - neutral waveform: work not yet completed; - accent waveform clipped from the left: exact backend progress fraction; - vertical accent boundary: current progress point. After completion, the waveform returns to the normal extracted overview with onset markers. ## UX flow A new `Start here` panel in the left sidebar makes the flow explicit: 1. Load audio. 2. Set controls. 3. Extract. 4. Review/export. The active step is updated from actual UI/job state. ## Limitations - The global `fraction` is a deterministic work-unit fraction: Demucs chunks when available, otherwise one boundary unit per stage. It is not elapsed-time prediction. - Demucs progress is chunk-accurate for the internal split chunks used by the Python API path. - Non-Demucs stages currently expose exact boundary progress only; adding finer-grained progress to onset detection and clustering requires breaking those algorithms into explicit callback-reporting loops.