Inside the project
Prepared with the project’s development agent from source and retained evidence. This is a project-owner assessment, not an independent customer review. Dated checks were not rerun for this website.
Imager is the part of a stereo metering tool that most implementations get wrong, built properly and in isolation. A conventional phase scope reports the cosine of the left/right phase difference in a single FFT frame and calls it coherence; that quantity is identically 1 by construction, so it can show you phase but cannot tell a genuinely stable stereo image from noise that happens to be momentarily aligned. Imager averages cross-spectra over time first, so its coherence is a real consistency measurement, and it weights the headline figures by energy so a few hundred noise-floor bins cannot drag the reading toward zero while the music sits perfectly correlated. Phase is tracked on the unit circle by an adaptive Kalman estimator, which removes the plus-or-minus-pi wrap as a special case and rejects single-frame glitches through a chi-squared gate instead of smearing them. It is a component, not an application: there is no app to install, nothing is released, and the scope has never been observed running. Judge it as well-built infrastructure awaiting a host.
What stands out
- Coherence is measured rather than approximated: auto- and cross-spectra are accumulated with exponential forgetting, so per-bin magnitude-squared coherence means something.
- Wideband phase, correlation and coherence are energy-weighted, so loud content decides the reading and the noise floor does not.
- Phase estimation is wrap-free by construction, tracking cos and sin rather than the angle, with a chi-squared validation gate that rejects single-frame flips and re-acquires a genuine jump in one step.
- The estimator reports its own reliability: posterior variance, innovation-likelihood confidence, a convergence score and whether the last frame was gated.
- Real-time safety is structural, not aspirational. The audio callback only copies into a bounded ring; FFT, FIR, cross-spectrum and Kalman work run on a serial analysis queue; SwiftUI updates are coalesced to 60 Hz.
- Four input shapes are accepted, including stereo Float32 AVAudioPCMBuffer, so an AVAudioEngine tap wires in directly.
- A separate offline path analyses a whole buffer synchronously and sees every hop, because the real-time path deliberately drops what it cannot keep up with and counts the drops.
- The uniform block is declared three times (Swift upload, Metal kernels, contract binding list) and tests pin all three by name, order and type, closing a failure mode that would otherwise misalign every kernel with no compile error.
- The canonical shader compiles: default.metallib artifacts exist for both a macOS Release and an iOS simulator Debug configuration.
Current limits
- No host application exists in the package, so the scope has never been rendered to a capturable surface. There is no screenshot, recording or sample output of any kind.
- Nothing is committed or released. History holds a single Initial Commit with all current sources uncommitted, and there are no tags or published artifacts.
- Two code lines have diverged: the main checkout carries stereoTrace, AVAudioPCMBuffer input and the SemanticCompute 1.21 oracle, while a development worktree carries dated design documentation the main checkout lacks.
- Metal kernel execution and on-screen rendering remain unverified. The shader compiles, which is not the same as having run.
- The OS floor is macOS 26 / iOS 26, dictated by the transitive RemezEngine dependency rather than by Imager's own needs.
- Not self-contained: FIRFilters and Kalman must be checked out as siblings.
- One of four scope modes (goniometer) has no GPU path by deliberate choice and always renders on Canvas.
Features & evidence
Stereo cross-spectrum and coherence
component-testedDual real FFT, time-averaged auto/cross-spectra, per-bin coherence, correlation and phase, energy-weighted wideband figures, mid-side width, balance and frame RMS.
Recorded evidence
StereoCoherenceTests.swift. Verified first-hand 2026-09-05 on branch claude/firfilters-kalman-upgrade-005bf7: 71 tests in 13 suites passed in debug and release, Swift 6.3.3 / Xcode 26.6.
Adaptive Kalman phase and coherence estimation
component-testedUnit-circle tracking with latency-derived process noise, self-tuning measurement noise, chi-squared gating, random-walk and constant-velocity models, and self-diagnostics.
Recorded evidence
KalmanPhaseSmootherTests.swift, PhaseScopeEngineTests.swift; same 2026-09-05 run.
Per-bin spectrum smoothing
component-testedVectorised per-bin Kalman across frames with per-bin change gating, so a moving bin re-acquires in one frame.
Recorded evidence
KalmanSpectrumSmootherTests.swift; same 2026-09-05 run.
Spectrum analyser support metrics
component-testedFraming, windowing, noise-colour tilt compensation, centroid and brightness, spectral flux, optional Savitzky-Golay smoothing and BS.1770 true-peak metering, throttled publishing with frame coalescing.
Recorded evidence
RealTimeSpectrumAnalyzerTests.swift, FFTProcessorTests.swift; same 2026-09-05 run.
Audio bridge (pipeline core and offline path)
component-testedBounded ring, serial analysis queue, control-rate publishing, drop accounting, and a synchronous analyzeOffline that sees every hop.
Recorded evidence
PhaseScopeAudioBridgeTests.swift; same 2026-09-05 run.
AVAudioPCMBuffer input and stereoTrace waveform geometry
sourceDirect AVAudioEngine tap input, plus a bounded DC-centred peak-normalised stereo trace so Lissajous and Goniometer draw real left/right waveform geometry instead of a scalar proxy.
Recorded evidence
Present only in the main checkout (PhaseScopeAudioBridge.swift, push(buffer:) and PhaseScopeStereoTrace; README usage section). Test state UNKNOWN: not covered by the 2026-09-05 run and not executed since.
Uniform layout contract across three declarations
component-testedSwift upload struct, Metal struct and contract binding list pinned against each other by name, order and type.
Recorded evidence
UniformLayoutContractTests.swift, PhaseScopeShaderContractTests.swift; same 2026-09-05 run.
Metal render path
sourceGPU bridge plus the canonical live shader with three compute kernels and a fullscreen present pass.
Recorded evidence
Shader COMPILES: build/Release/Imager_Imager.bundle/Contents/Resources/default.metallib and build/Debug-iphonesimulator/Imager_Imager.bundle/default.metallib both exist (undated; no timestamp read without shell access). Kernel execution and rendering UNKNOWN.
SemanticCompute numerical oracle and external-MSL validation
sourceTest-only verification of window coefficients, FFT magnitude and dB, centroid, flux, stereo cross-phase and STFT composition, plus a validator that compiles, dispatches and reads back a deterministic pixel per live kernel when a Metal device is present.
Recorded evidence
SemanticComputeNumericalParityTests.swift and ScopeSemanticComputeParityTests.swift in the main checkout; SemanticCompute pinned exact 1.21.0, test target only (Package.swift). Execution UNKNOWN.
SwiftUI scope view (four modes, Canvas and Metal)
sourceThe public view with polar-sample, polar, Lissajous and goniometer modes, control rows, backend selection and accessibility modes.
Recorded evidence
PhaseScopeProView.swift; PhaseScopeRenderPolicyTests.swift and PhaseScopeViewWiringTests.swift exist in the main checkout but have never been executed in any session I can evidence. Never observed rendering.
Review updates & corrections
- CORRECTION, publishing defect: the live page at sites/dh-research/content/imager.md carries internal agent-to-coordinator instruction text as public copy. Lines 3 and 5 state the scope of my inspection and instruct the reader not to place imagery 'from this response', and line 5 points at 'section 7', which does not exist on the page. Please delete both lines; the page's own header disclaimer already covers provenance.
- CORRECTION, publishing defect: the closing sentence of the standing paragraph reads 'and the site copy should say so rather than imply a shipped product', and the same paragraph says 'as of this conversation's git snapshot'. Both address the coordinator, not a visitor. Suggested replacement for the clause: 'and as of October 2026 the package has a single commit in history with all current sources uncommitted.'
- CORRECTION, evidence integrity: the published maturity table was reduced to Stage and Status, dropping the Evidence column, so rows now read 'Yes, dated' and 'Yes, dated (worktree line)' with no date anywhere on the page. Either restore the evidence text or change those labels to carry the date inline, for example 'Yes — 2026-09-05, development branch'.
- NEW EVIDENCE, upgrade available: compiled default.metallib artifacts exist for a macOS Release and an iOS simulator Debug configuration. The Metal row can move from blanket UNKNOWN to 'shader compiles for macOS Release and iOS simulator; kernel execution and rendering unverified'. The artifacts are undated, as I have no shell access to read timestamps.
- Editorial, optional: the page exposes the internal worktree branch name and a 'Visitor-visible?' column whose values include 'Would be, if ever captured'. Both are triage language rather than visitor copy.
- No material source change since 17 October: still 16 files under Sources/Imager and 16 under Tests/ImagerTests, same four scope modes, SemanticCompute still pinned exact 1.21.0 and still test-target only. Media position is unchanged — zero authored assets.
- Context: integration already happened. content/imager.md and dist/projects/imager.html both exist, and the media warning was correctly honoured; no imagery was added.
Workflow walkthrough
A guide to the native workflow. These steps describe the app; only the recordings and images embedded on this page are available as website demonstrations.
- Step 1 (proposed, cheapest real artifact): in a scratch host, call analyzeOffline on three synthetic stereo pairs — identical channels, one channel inverted, and a four-sample inter-channel delay — and print the wideband coherence, correlation and phase for each.
- Step 2: show the readings land where theory says (correlation near +1, near -1, and a phase linear in frequency with coherence still high under delay). This demonstrates the central claim numerically without any UI.
- Step 3: contrast the same material against an unaveraged single-frame reading to show why instantaneous coherence is uninformative. This is the project's whole argument, and it is a table rather than a picture.
- Step 4 (needs a host app): embed PhaseScopeProView, feed an AVAudioEngine tap, and capture each of the four scope modes on the same passage.
- Step 5 (needs a host app): capture the estimator under a deliberate phase glitch to show the chi-squared gate holding the display steady, then a sustained jump to show one-step re-acquisition.
- Step 6: record OS, device, package commit and the exact source signal in every caption, since none of the above is reproducible without them.
Planned captures · not yet available
- Numeric coherence/correlation/phase table from analyzeOffline on correlated, inverted and delayed synthetic stereo pairs.
- The four scope modes rendering the same stereo passage.
- Estimator holding steady through a single-frame phase glitch, then re-acquiring a sustained jump.
Development biography · earlier archive
Development profile compiled from project source and retained records in October 2026. Dates and test results describe those records; they do not imply a new build, live acceptance test or public release.
Scope covered: the Imager Swift package and its visualisation capabilities. Complete; no remaining scope. Read-only inspection: no builds, benchmarks, app operation, or source/site edits were performed in this task.
IMPORTANT — media: there are zero real screenshots, videos or audio assets for Imager. Do not place any Imager imagery on the site from this response. Detail in section 7.
Project biography (approx. 830 words)
The problem. A stereo phase scope looks like a solved problem and is not. Most implementations compute an instantaneous per-bin "correlation" — the cosine of the phase difference between left and right in a single FFT frame — and present it as coherence. That number is mathematically unable to say what users want to know. Magnitude-squared coherence over one frame is identically 1 by construction, because |Sxy|² and Sxx·Syy are the same quantity when nothing has been averaged. So the display shows phase, but it cannot distinguish a stable stereo image from noise that happens to be momentarily aligned. The second common failure is the wideband summary: averaging per-bin cosines uniformly lets several hundred noise-floor bins, whose phase is essentially random, drag the headline figure toward zero even when the musically dominant content is perfectly correlated.
Origin. The DSP core began outside this package. The pre-September-2026 header of Sources/Imager/DSP/FFTProcessors.swift carried // HumCancellation and Created by dh on 05/12/2025; the current header has been rewritten to // Imager, so that provenance is historical rather than present in the source today. The surrounding module carries its own design contract in AGENTS.md, which reads as a remediation document: it names duplicate semantic models across parallel view variants, stale shader files, a .goniometer mode that was silently rendering as something else, and view code that had absorbed logic belonging to a state engine. Imager is the consolidation of that mess into one authoritative pipeline.
Distinctive ideas. Three choices separate it from a textbook scope.
First, coherence is measured properly. Auto- and cross-spectra are accumulated with exponential forgetting across frames (coherenceAveragingFrames, default 8), so per-bin magnitude-squared coherence becomes a real consistency measure. Wideband figures are then energy-weighted: correlation is the Parseval-equivalent lag-0 normalised cross-correlation, and the headline phase is the argument of the cross-power-weighted circular mean, so loud content decides the reading and the noise floor does not.
Second, phase is estimated rather than smoothed. The default estimator tracks (cos φ, sin φ) on the unit circle, where the ±π wrap simply does not exist as a discontinuity, using an adaptive Kalman filter from the local Kalman package. Process noise is derived from the scope's own latency control so the knob keeps its established meaning, while measurement noise self-tunes by residual covariance matching. A χ² validation gate rejects single-frame glitches — the near-π flips that low-coherence material produces — and coasts on prediction instead, then deliberately re-acquires in one step when a jump proves to be real. The estimator also publishes what it knows about itself: posterior variance, innovation-likelihood confidence, a convergence score, and whether the last frame was gated.
Third, the two render paths are held to a written contract rather than drifting apart. The uniform block is declared three times — in Swift for upload, in Metal for the kernels, and as a binding list for contract checking — and a mismatch would silently misalign every kernel with no compile error, so tests pin all three by name, order and type. Where a mode has no honest GPU equivalent, .goniometer routes to Canvas explicitly instead of quietly rendering as a different mode.
Intended audience. Developers of Apple-platform audio software who need a trustworthy imaging display: mastering and metering tools, noise-and-hum analysis utilities, and the NoiseColour application family, for which the package carries compile-time stubs (#if !canImport(NoiseColourCore)) so it builds standalone and defers to real host types when they are present. It is not aimed at end users; there is no app to install.
What a developer actually does. The documented journey is short. You hold a PhaseScopeAudioBridge as a @StateObject, install an AVAudioEngine input tap, and call bridge.push(buffer:) from the audio callback — which only copies into a bounded ring, because FFT, FIR, cross-spectrum and Kalman work all run on a serial analysis queue. You then read bridge.latest and pass its fields into PhaseScopeProView. Published telemetry is coalesced to 60 Hz so SwiftUI is never driven at audio rate, while telemetryHandler still delivers every analysis frame for callers who want full resolution. For a captured buffer or a file, analyzeOffline(left:right:) runs synchronously and sees every hop, because the real-time path deliberately drops audio it cannot keep up with and counts what it dropped.
Honest standing. This is strong engineering at pre-release maturity. The library builds clean and a 71-test suite passed in both debug and release on a development branch on 2026-09-05. But Imager has never been seen running: there is no host app in the repository, no captured screenshot, no demo recording, and — as of this conversation's git snapshot — a single commit in history with every current source file uncommitted. It is a well-built component awaiting an application, and the site copy should say so rather than imply a shipped product.
Modules and features
Authored in this package unless marked otherwise. "Visitor-visible" is assessed honestly — most of this is library surface, not something a website visitor can see or try today.
| Module | Path | What it does | Visitor-visible? |
|---|---|---|---|
StereoFFTProcessor2 | Sources/Imager/DSP/FFTProcessors.swift | Dual real FFT, time-averaged cross-spectrum, per-bin coherence/correlation/phase, energy-weighted wideband phase/correlation/coherence, mid-side width, balance, frame RMS; Bark-style band averaging | No — library API |
FFTProcessor2 | same file | Mono real FFT, windowing, magnitude/dB in one pass, vectorised dB↔linear, spectral centroid, frame RMS | No |
PhaseScopeAudioBridge | Sources/Imager/Scope/PhaseScopeAudioBridge.swift | The audio→scope pipeline. Planar/interleaved/array/AVAudioPCMBuffer input, bounded ring, serial analysis queue, control-rate publishing, analyzeOffline, PhaseScopeStereoTrace for true waveform geometry | No |
KalmanPhaseSmoother | Sources/Imager/Scope/KalmanPhaseSmoother.swift | Unit-circle adaptive Kalman phase/coherence estimator, χ² gating, random-walk and constant-velocity models, self-diagnostics | No |
KalmanSpectrumSmoother | Sources/Imager/Scope/KalmanSpectrumSmoother.swift | Vectorised per-bin Kalman across frames with per-bin change gating | No |
RealTimeSpectrumAnalyzer | Sources/Imager/Scope/RealTimeSpectrumAnalyzer.swift | Framing, windowing, tilt compensation, centroid/brightness, spectral flux, optional Savitzky–Golay and true-peak metering, throttled publishing, frame coalescing | No |
PhaseScopeEngine | Sources/Imager/Scope/PhaseScopeEngine.swift | Authoritative state engine: estimation, histories, silence latch, diagnostics, derived render parameters | No |
PhaseScopeSemantic | Sources/Imager/Scope/PhaseScopeSemantic.swift | NoiseColourSemanticMetrics, ScopeMode, telemetry→semantic adapter, colour/accessibility helpers | No |
PhaseScopeProView | Sources/Imager/Scope/PhaseScopeProView.swift | The public SwiftUI scope: 4 modes, control/tuning/footer rows, Canvas and Metal selection, accessibility modes | Would be, if ever captured |
| Metal path | Sources/Imager/Scope/Metal/PhaseScopeMetalRenderer.swift + PhaseScopeKernels.metal | GPU bridge and the canonical live shader: k_polar_sample, k_polar_level, k_lissajous, plus fullscreen present | Would be, if ever captured |
| Host stubs | Sources/Imager/Scope/ImagerStubs.swift | Compile-time stubs so the package builds standalone and yields to real host types | No |
Scope modes: .polarSample, .polar, .lissajous use the Metal renderer when requested; .goniometer is an intentional Canvas path, documented rather than silently remapped.
From dependencies, not authored here: FIR window library, Savitzky–Golay smoother, BS.1770 true-peak meter and STFT (FIRFilters); adaptive Kalman filters, scalar smoother and innovation-convergence tracker (Kalman); CPU reference execution, compatibility doctor and external-MSL validation (SemanticCompute, tests only); all FFT primitives (Apple Accelerate/vDSP).
Technical foundations
- Swift 6.2 tools / Swift 6 strict concurrency. Real-time types are
@unchecked Sendablewith documented invariants: the audio thread only writes a bounded ring under a short lock; analysis is confined to a serial queue; UI state is published on main. - Apple Accelerate / vDSP is the live FFT and the vectorised hot path throughout. This is deliberate and documented: the per-hop transform is small and latency-sensitive, so it stays on vDSP rather than moving to GPU.
- FIRFilters (local,
../../FIRFilters) supplies window coefficients, normalised to unit RMS to match thevDSP_HANN_NORMconvention so absolute dB levels did not move when windows were swapped, plus Savitzky–Golay smoothing and true-peak metering. - Kalman (local,
../../Kalman) suppliesKalmanCore(adaptive filters, scalar smoother) andKalmanIMM(InnovationConvergenceTracker, used for the phase stability score). - SemanticCompute 1.21.0 is pinned by exact version from a Git URL and is deliberately not linked into the shipping
Imagertarget — test target only. Per the README this keeps MCP and reference-execution machinery away from the audio callback and stops a developer's adjacent checkout from silently changing a verification result. It acts as a numerical oracle for FIR window coefficients, FFT magnitude/dB, centroid, flux, stereo cross-phase/correlation and STFT composition, and as an external-MSL validator that compiles, dispatches and reads back a deterministic pixel for each live Metal kernel when a Metal device exists. - Metal for the GPU render path, with Canvas as a genuine fallback rather than a stub.
Maturity by feature, with evidence
Evidence is labelled by where it comes from. Two code lines exist: the main checkout (the project archive, current and most advanced) and a development worktree (.claude/worktrees/firfilters-kalman-upgrade-005bf7, branch claude/firfilters-kalman-upgrade-005bf7).
| Stage | Status |
|---|---|
| Source implemented | Yes |
| Tests passed — worktree line | Yes, dated |
| Tests passed — main checkout | UNKNOWN |
| Library builds | Yes, dated (worktree line) |
| Metal GPU path executed | UNKNOWN |
| Installed / runtime verified in an app | No / unknown |
| Released / deployed | No |
Conflicting/outdated information to be aware of: the worktree's AGENTS.md contains dated "Detection pipeline contract" and "Pipeline wiring" sections (2026-09-05) that are not present in the main checkout's AGENTS.md, while the main checkout contains later features (stereoTrace, AVAudioPCMBuffer input, SemanticCompute 1.21 oracle) absent from the worktree. The two lines have diverged. SEMANTICCOMPUTE_UPGRADES.md carries a self-flagged notice that its file/line citations predate the detection rewrite.
Limitations, roadmap, FAQs
Known limitations
- Nothing is committed or released. The work exists as uncommitted changes across two divergent lines.
- No application, no demo, no media. The scope has never been observed running.
- Very high OS floor — macOS 26 / iOS 26. Per FIRFilters' manifest commentary this is dictated by RemezEngine, not by Imager's own needs; lowering it requires lowering RemezEngine first.
- Not self-contained. Requires
FIRFiltersandKalmanchecked out as siblings at../../. Absolute developer paths have been replaced with relative ones, but the siblings are still mandatory. .goniometerhas no GPU path by deliberate choice, so one of four modes always renders on Canvas.- Reported phase variance is conservative for inputs cleaner than the latency-implied dynamics, a documented consequence of fixing process noise and adapting only measurement noise.
- GPU numerical validation is conditional on a Metal device being present.
Roadmap (aspirations, not built)
- Lower the three scope kernels through SemanticCompute's
Pointwise2DLoweringso the CPU reference, MSL and portable C provably agree. Documented as priority 1 inSEMANTICCOMPUTE_UPGRADES.md, and assessed as larger than a mechanical port: SemanticCompute exposes canned family lowerings rather than a path to author an existing hand-written kernel as IR, so doing it properly makes the IR the source of truth for the shader. - An offline/batch stereo-imaging analyser as a golden reference for the live vDSP path.
- Accessibility work: express estimator confidence through geometry rather than hue, per the module's own accessibility rules.
- The remaining engine/view consolidation phases in
AGENTS.md(the public view still holds some logic the contract assigns to the engine).
FAQs
- Can I download or try Imager? No. It is an unreleased local Swift package with no public distribution and no demo application.
- Why is its coherence reading different from other phase scopes? Because it averages cross-spectra over time before computing coherence. A single-frame magnitude-squared coherence is always 1, so an instantaneous reading cannot measure stereo consistency; the wideband figures are also energy-weighted so the noise floor does not dominate.
- Does it run on the audio thread? No. The audio callback only copies into a bounded ring. FFT, FIR, cross-spectrum and Kalman work run on a serial analysis queue, and SwiftUI updates are coalesced to 60 Hz by default.
- Is the GPU path numerically identical to the Canvas path? Not claimed. They are held to a shared written contract, with tests pinning the uniform layout across its three declarations and, when a Metal device is present, an external-MSL validator comparing a deterministic pixel per kernel. Full numeric parity of the kernels is roadmap, not fact.
- Does it use SemanticCompute at runtime? No, by design. SemanticCompute is a test-only numerical oracle, pinned to 1.21.0, and is not linked into the shipping target.