RFC-007 — Key moment detection
Status · Decided · closed 2026-05-03 → ADR-024 TA anchor · components/render-system · contracts (audio scalars in payload) Related · PRD-005 Video Editor · RFC-006 Audio system Closes into · ADR-024 Why this is an RFC · Audio swells and overlay markers must fire at the same frames — when the music swells, the record-flash appears, the anomaly bar peaks. PRD-005's sharpest threat (audio reads as decorative) is defeated specifically when the visual and audio events are demonstrably synchronised. They share a single per-frame intensity signal. The algorithm that produces that signal is open: multiple plausible approaches, each detecting different kinds of "moment."
The question
Given a data scalar time series — say, daily SST anomaly for the North Atlantic over a year — produce a per-frame intensity signal that captures moments worth marking. The signal is consumed by:
- Audio (RFC-006): the per-frame intensity drives crossfading between musical stems.
- Overlays: the intensity threshold determines when a record-flash, anomaly-grow, or named-event annotation appears.
What constitutes "a moment worth marking"? Several plausible signals exist:
- Statistical peaks — values exceeding the mean by more than 2 standard deviations.
- Record values — values exceeding all prior values in the time series.
- Threshold crossings — values crossing a user-defined or scientifically-meaningful boundary (e.g., bleaching threshold for SST).
- Inflection points — moments where the rate of change reverses or accelerates significantly.
Each captures a different kind of moment. A complete system uses all four, combined into one intensity signal. The deliberation is the combination — how to weight, scale, and merge them into a single 0.0–1.0 value per frame.
Use cases
- Year of SST anomaly. Statistical peaks fire mid-summer (above-mean warming days). Records fire in late August (warmest day ever). Threshold crossings fire on the days exceeding the 1.5°C warming line. Inflection points fire at the start and end of heat-wave periods.
- Decadal sea-level rise. Records fire constantly in recent years (every month is a new high). Statistical peaks barely fire (the trend dominates the noise). Threshold crossings on integer-cm marks. Inflection points on acceleration changes.
- Year of sea-ice extent. Records fire at the September minimum. Statistical peaks rarely fire (seasonal signal dominates). Threshold crossings at scientifically-meaningful extents. Inflection points at freeze-up and break-up dates.
- A calm time series. No records, few peaks, no significant inflections. Intensity stays low; audio remains calm; overlays don't intrude.
The four use cases want different relative weights on the four signals. A unified algorithm with sensible defaults plus per-recipe tuning is the goal.
Goals
- One algorithm, one signal, both audio and overlays consume the same value for the same frame.
- The signal is deterministic — same time series produces the same per-frame intensities.
- The signal is calm-by-default — without significant events, intensity stays low.
- The signal scales gracefully across different data ranges (SST anomaly vs. sea-level vs. ice extent).
- The four signal types are inspectable individually (the editor can show which kind of moment was detected, for debugging and overlay labelling).
Constraints
- Determinism — same time series + same parameters → same intensity signal (TA constraints).
- Single source of truth for moments — audio and overlays compute from the same algorithm output. No separate moment-detection paths (TA constraints).
- No machine learning models in v1 — the algorithm must be classical (statistical, threshold-based). Trainable models are deferred.
Proposed approach
Four detectors, each producing an intensity contribution; a weighted sum, clamped to 0.0–1.0.
For each frame f in a time series of values v[0..N]:
peaks_score(f) = abs(v[f] - mean) / (k * stddev) # k=2 for the 2σ convention
records_score(f) = 1.0 if v[f] >= max(v[0..f-1]) else 0.0
threshold_score(f) = 1.0 if v[f] crosses a defined threshold this frame else 0.0
inflection_score(f) = abs(d²v/dt² at f) normalised to [0, 1]
intensity(f) = clamp(
w_peaks * peaks_score(f) +
w_records * records_score(f) +
w_threshold * threshold_score(f) +
w_inflection * inflection_score(f),
0.0, 1.0
)
The weights w_* are configurable per recipe (or per export), with sensible defaults:
Records get higher weight than inflections because record events are the most viscerally meaningful in climate-communicator use cases.
The intensity signal is temporally smoothed with a small window (e.g., 3-frame Gaussian) before being consumed, so that a single anomalous frame produces a brief audio swell rather than a one-frame click.
Each detector also produces a labelled event when its score crosses a high threshold (>0.7). The overlay system uses the labels to choose annotation type:
- High peaks_score → "anomaly indicator grows"
- High records_score → "record flash" + label "highest [variable] ever measured"
- High threshold_score → "named event marker"
- High inflection_score → "trend change" subtle marker
Same signal drives audio crossfading; the labels drive overlay choice.
Alternatives considered
Alternative: just use anomaly directly
Define intensity(f) = abs(anomaly(f)) normalised to [0, 1]. Skip the four-detector composition.
Rejected because anomaly alone misses records (a value 1.5σ above mean that is also a record is more meaningful than a 2σ value that is unremarkable in a long record). Records and anomalies are different kinds of significance, and the system should treat them differently. The composition is a small cost for a meaningful gain in expressiveness.
Alternative: ML-based event detection
Train a model on labelled "interesting moments" in time series; use the model output as the intensity signal.
Rejected for v1. Adds a model-training pipeline, model versioning, dataset curation — entire infrastructure. The classical four-detector approach handles the use cases cleanly. Revisit if classical approach proves insufficient.
Alternative: single threshold per recipe
Set one threshold; intensity is binary (event / no event).
Rejected because audio crossfading needs continuous values, not binary. Overlays could use binary, but coupling audio and overlays through a single signal forces continuous output.
Alternative: data-source-specific algorithms
Different algorithms for SST, sea level, ice extent — tuned to each variable's typical statistics.
Rejected because the four-detector model with configurable weights handles per-source tuning at the weights level rather than the algorithm level. Keeps the algorithm uniform across sources; just the parameters change.
Trade-offs
- Default weights are a choice that won't be right for every recipe. Mitigated by per-recipe overrides; documented as expected.
- Smoothing window is a magic number. 3-frame is conservative; longer smoothing makes audio mushier; shorter makes audio jittery. Tuneable.
- The intensity signal is a single channel. A more nuanced design might output multiple channels (peaks-channel, records-channel, etc.) to drive multi-instrument arrangements. Reserved for a future iteration.
- Inflection detection is sensitive to noise. Second derivatives amplify noise. Mitigated by smoothing the time series before computing the inflection score.
Open questions
- Is the threshold-crossing detector worth the complexity? It requires per-recipe configuration of meaningful thresholds. Default to "off" for recipes that don't define them?
- How does this interact with multi-source recipes? Currently the algorithm assumes one scalar time series. A composite recipe with two sources may want to combine their signals — additively, multiplicatively, or pick the dominant one?
- Should the algorithm produce its output once at export time, or per-frame on demand during preview? Probably once at export, cached. Preview can use a cheap approximation.
- Storage: do we store the per-frame intensity signal as part of the export bundle, or recompute every time? Storing makes the export reproducible without re-running the algorithm; recomputing keeps the bundle small.
How this closes
- ADR-NNN — Key moment detection algorithm. Locks the four-detector composition, the default weights, the smoothing window, and the per-recipe override mechanism.
Links
- TA — components/render-system · contracts (audio scalars)
- Related PRDs — PRD-005 Video Editor
- Related RFCs — RFC-006 Audio system (consumes this signal)