The Peak Finder Overlay
An operator looks at a camera frame and sees ridges. The overlay puts a name on every one of them — San Jacinto Peak 10,787 ft, standing on the actual pixels of San Jacinto — with a bearing and a distance that can be read straight into a radio. And once the picture and the model agree that well, agreement becomes a tool: click any point in the photo and the map shows you where on Earth it is.
What ships today, in one breath: labeled terrain blended over the 60° view and over the whole 360° strip, an Explore mode on both where you take the engine's controls, click-to-locate that turns a pixel into a map point with a copyable report, an automatic self-calibration that runs in the operator's browser and shares its result with the whole fleet, a one-click zoom-optics calibration that steps a real camera and measures its lens, and a hand-calibration escape hatch gated to calibration-authorized accounts.
This page tells the story in eight chapters. Every chapter has one idea and one thing to play with. The deep, measured details fold away under Go deeper — open them when you want the evidence.
Chapter 1 · A twin camera, five numbers
PeakFinder (a third-party engine) owns a 3-D model of the planet's terrain. Our overlay does no terrain math of its own: it stands a virtual camera in the tower, points it with the photograph's own numbers, and lays the render over the photo, pixel-pinned and blended. If — and only if — the virtual camera truly matches the real one, ridge falls on ridge and the render melts into the picture.
Five numbers do the aiming, and each one fails with its own recognizable fingerprint: azimuth (a sideways slide, same everywhere), tilt (a vertical slide, same everywhere), field of view (edges peel while the centre holds), eye position (near terrain moves, far terrain doesn't), and the boresight — the star of chapter 3. Learn the fingerprints by breaking them:
All five numbers right — the trace disappears into the skyline. This is the whole contract.
A misalignment report that names the fingerprint — "it's slid sideways everywhere" vs "the edges peel" — is already half the diagnosis.
Go deeper: the five inputs and what each one cost us
| # | Input | Where it comes from | When it was wrong, what it cost |
|---|---|---|---|
| 1 | Azimuth (pan) | stamped on each image at capture | Aiming from the camera's current pan instead of the photo's showed the wrong direction after every patrol move |
| 2 | Tilt | same image stamp — sign-inverted for the engine | The engine's altitude axis runs opposite to the camera's tilt; passing it through unchanged mis-aimed every camera by 2× its own tilt — the classic "needs manual calibration" illusion |
| 3 | Field of view | the lens model + zoom, through the optics table | A documented "8° minimum" turned out to be fiction (the renderer is exact to at least 1°); clamping at 8 broke the whole top ⅔ of the zoom range |
| 4 | Eye position | site coordinates + elevation + tower height | Chapter 2 |
| 5 | The boresight | solved from skyline evidence | Chapter 3 |
The tilt inversion deserves its own warning label: the engine's altitude
parameter is inverted relative to true elevation (altitude = −tilt is the
measured contract). Any NEW consumer of the aim — the click-to-locate ray
was one — must negate it back, or every reading is off by 2× tilt… invisibly,
as long as the camera happens to sit level.
Chapter 2 · Stand at the right height
Four of the five inputs produce constant errors, and a constant can be
calibrated away. Eye height is the exception: a camera on a 30 m mast sees
near terrain differently than one on the ground, and no constant fixes a
distance-dependent error. That is why aboveGroundLevel is a real data
field, not a fudge factor.
At Boucher Hill the real value is 30.48 m (a 100 ft mast). Ignore it and the far calibration ridge moves 0.03° (harmless) while the near forest line moves ~1.4° — dozens of pixels — in the OPPOSITE direction the boresight would need. One constant cannot satisfy both distances; only the true eye height can.
The rule of thumb: atan(height / distance) — 30 m is 0.03° against a
ridge 60 km out and ~1.4° against the forest line 1.2 km out. The value
lives in the CameraConfig system of record and rides along on camera static
metadata.
Chapter 3 · The mount error solves itself
Every camera is bolted to a mast by human hands: the pan encoder's zero is not quite north, the mount is not quite level. That 0.3–1.0° remainder is the , it is different for every camera, and no metadata field records it. It can only be discovered the rifle-scope way: look at where the shots land.
Here, the "shots" are the skyline. The photo shows where the ridge is; the terrain model says where the aim claims it is; the constant difference across the whole frame is the mount. This happens at runtime, in the operator's own browser, the first time the overlay opens on an unsolved camera — nobody clicks anything. Watch it happen, slowed down:
act 1/6 · The frameA frame lands. The white trace is the terrain model aimed with the photo’s own numbers — and it does not sit on the ridge. That gap IS the mount error, hiding in plain sight.
The one idea worth keeping: forty measurements, one explanation. Noise scatters; a mount error repeats. A correction solved from one ridge that also fixes every other ridge can only be the mount.
Go deeper: bad weather, and the gates that refuse to guess
The film shows a clear day. Reality has fog, clouds sitting on the ridge, and night — mornings that must fail safely, because a wrong boresight corrupts every frame while a rejection costs one retry. Same solver, with the weather against it:
Yellow dots: ridge edges. Red: cloud bottoms the v1 detector mistook for skyline (they sit degrees above everything else — enough of them and the 80 % majority gate refuses the whole frame, which is exactly what the real fleet measured all one cloudy morning). Green: the same columns after the cloud-aware pass re-probes BELOW the cloud. A rejection costs nothing — the solver retries on a better frame.
Every gate exists because of a real morning: ≥ 15 columns of evidence (fog fails here, harmlessly), 80 % must agree (a cloudy morning was rejected wholesale because a topmost-edge detector latched cloud bottoms in ~20 % of columns — the fix was teaching the detector that the true skyline is the lower edge of sky, not loosening the gate), MAD ≤ 0.2°, and |correction| ≤ 3° (anything bigger is a data problem, not a mount). Failed solves retry every 10 minutes while the overlay is open.
The worked example: boucher-cam08's shipped seed came from this exact
pipeline run offline — 4 frames across 31 minutes agreeing at
+0.39/+0.34/+0.35/+0.35°, pooled to dAlt +0.37° from 91 samples at MAD
0.065°, cross-validated at a second zoom within 0.05°.
Chapter 4 · Solved once, corrected everywhere
A solve is only useful if every operator's browser applies it. Boresights live in a small fleet store with precedence rules enforced twice — client and server (a fleet-wide aim correction is not taken on faith, even from a signed-in client; the backend re-runs every quality gate itself):
- Offline solves (the deliberate multi-pose pipeline) outrank everything.
- Manual overrides (a person dragged the lines onto the ridge) outrank…
- Runtime solves (the automatic chapter-3 pipeline), newest wins within a tier, and nothing ever erases a higher tier.
scenario 1 / 7 · score 0
The fleet store has an OFFLINE multi-pose solve. Your browser solved the same camera an hour ago.
The manual escape hatch lives in the overlay's settings menu (Alignment → Fine-tune by hand): drag the lines onto the ridges, arrow keys nudge 0.05°, Save shares it fleet-wide, Revert forgets it in one click (the backend refuses to let a revert delete measured solves — evidence is not user data). It is deliberately an escape hatch, not a workflow: an era of hand-tuning once quietly masked the 2×-tilt metadata bug, so the system solves from evidence, gates hard, and shows its provenance in the menu's alignment chip ("Automatic · fleet-wide" / "Manual · this browser").
Both the affordance and the write are gated to calibration-authorized
agencies — the same allow-list as the pano-offset calibration
(PanoCalibrationOptions:CompanyList); everyone else sees the chip but no
edit actions. Every save and revert is logged with who did it. And every
entry carries a geometry version: when the aiming pipeline itself
improves, old solves — corrections for errors that no longer exist — are
orphaned all at once instead of silently double-correcting.
Chapter 5 · The 360 is six photographs, not a cylinder
The pano strip looks like one seamless panorama. It is actually six 60° photographs butted side by side — the capture rig shoots six frames at fixed pan steps and the stitcher concatenates them, full frames, no warp. A hexagon, not a cylinder. Overlay it with one wide render and the geometry disagrees inside every frame:
- Drag the strip sideways
- Watch the white line weave off the ridge inside every frame
- Then flip to six pinholes
Dashed verticals are the stitch seams. The cylinder render is exact at every seam and every frame centre — and wrong by up to ~1.1° everywhere between, a “sawtooth” no calibration constant can remove. One perspective render per frame has the same tan-mapping as its photograph, so the error is zero by construction. (Vertical angle scale stretched for visibility.)
So the shipped 360 overlay renders one perspective view per stitched
frame, each pinned to its frame's slot and translated with the scroll — a
facet's aim belongs to the slot, so scrolling never re-aims anything. Each
facet is geometrically identical to the 60° view, which means everything in
chapters 1–4 applies to the strip unchanged: the same boresight corrects all
six facets rigidly (one global constant, one manual fine-tune for the whole
ring), and each facet uses its frame's TRUE optical field of view, recovered
from the capture rig's stored zoom (panoZoom on camera metadata).
Go deeper: bringing six render engines up without them fighting
Six live WASM render engines on one page taught us, by measurement, that the SDK's bring-up is fragile in every way it could be: more than two concurrent inits poison panels permanently; the init callback fires before the instance accepts calls (and sometimes never fires at all, while the panel quietly renders anyway); and a panel that loses its first attempts must not stay blank forever. The shipped bring-up: at most two at a time, each turn released the moment its panel provably accepts calls; a probe (not the callback) decides readiness; adoption also triggers on evidence of rendering (a sized canvas buffer); an unsized panel is retried on a fresh canvas — fast three times, then quietly every two minutes, forever. Facets stay invisible until actually rendering (a loading canvas is black, and black × multiply would black out the photo), on-screen slots claim the queue before off-screen preloads, and nothing ever re-initializes on scroll. Measured result: a 2,000-px-wide strip fills all six facets in ~35 s; the visible slots come up in ~8–15 s warm.
Chapter 6 · Take the controls
The engine's only native interactions move its camera — the one thing alignment forbids. So the locked overlay ignores the pointer entirely, and Explore mode (on the 60 and the 360) is an explicit state: the photo dims behind a scrim, the render turns opaque and interactive — drag, zoom, tap a peak for its info sheet — and one loud button re-aligns to the camera. Behind it stands the keeper, a closed loop that re-asserts the aim forever, because the engine's loading journeys silently overwrite aim commands:
Drag the view sideways. Locked: the keeper snaps it home within a second — that snap IS the alignment guarantee, and it is also why a drag can never coexist with alignment. Explore: the keeper hands off and the scrim appears, because a detached render blended over a frozen photo must never be a visible state. The two sweep buttons replay a measured production bug: with the keeper live, every telescope probe was yanked home mid-read — 0/13, so runtime self-calibration silently never happened until the sweep learned to hold the keeper gate.
The keeper is the alignment guarantee and, twice over, the story's antagonist: it once yanked the solver's own telescope home mid-measurement (0 of 13 probes surviving — runtime calibration silently never happened until the sweep learned to gate it), and it would yank an exploring user the same way, which is why Explore freezes the aim at entry.
Chapter 7 · Click a mountain, get a map point
Alignment, made queryable. When the overlay is on, Locate on map (in the settings menu, both views) turns every click into geometry: the pixel's sight-line goes through the exact boresight-corrected aim the overlay renders with, marches across the terrain model (earth curvature and atmospheric refraction included), and the first terrain it strikes lands on the app's map — blue point, dashed sight-line, distance label — with a card you can copy straight into radio traffic:
- Click a mountain in the photo
- Watch the sight line march the map
- Copy the radio-ready report
The ray reuses the exact boresight-corrected geometry the overlay renders with — that is what makes a click trustworthy enough to read over a radio. In the app the marker is the same blue point, the report line adds elevation and a timestamp, and clicks work from the 60 and the 360 alike.
Two honesty rules shipped with it, both from measured failures. A click above every ridge says so ("marked the visible ridge on that bearing") instead of pretending precision. And the ray demands that near crossings clear it by a canopy allowance — the elevation model reads treetop heights in dense forest, so a forested foreground ridge stands ~15 m "taller" than the photo's terrain and once stole clicks aimed at mountains 50 km behind it (the "far peak says 1.6 miles" bug: try the toggle). The card's coordinates and elevation exist so every result can be sanity-checked at a glance.
Chapter 8 · The lens learns its own zoom
Zoom is the one dial the overlay has to take on faith. The camera reports a zoom value (1–9999), not an angle — and the mapping between that value and the lens's true field of view is undocumented. Datasheets pin exactly two points: full wide and full tele. Between them the app has always used an interpolation — a guess anchored only at its ends, and the reason a zoomed-in overlay could look subtly the wrong width even when perfectly aimed.
So the lens is made to measure itself. Calibrate zoom optics (settings menu, 60 view — for calibration-authorized accounts with control of the camera) runs a one-click sweep: hold the aim, step the zoom through a ladder, wait for a fresh frame at each rung, and ask the same question the boresight solve asks — except now the free variable is the frame's width. The skyline the photo shows and the skyline the terrain model predicts only lie on top of each other at one field of view. Find it, and that zoom value is measured forever:
act 1/6 · Two honest endsThe zoom value the camera reports is not a field of view — the mapping between them is undocumented. The datasheet anchors exactly two points: full wide and full tele. Everything between is a guess (the dashed line), and the overlay inherits its error at every mid zoom.
The one idea worth keeping: a curve belongs to a lens MODEL, not a camera. The mount error of Chapter 3 is bolted-on-by-hand unique; the zoom motor's profile is factory glass. One good sweep on one Q6075-E calibrates every Q6075-E in the fleet — and the map's viewshed cones (computed by the event pipeline from the same shared store) draw the measured width too.
Go deeper: five measured failures, five gates
The measurement earns trust the way the boresight did — by refusing to answer when it can't. Every gate below was a real failure on boucher-cam07 before it became a precondition:
- The readout lags the aim. The engine's telescope updates a render frame (or several) after an aim command; reading on a fixed timer returned the previous probe's values — 15/17 azimuths read stale sky on terrain that measures 40–90 km once settled. Reads now wait until the aim readback matches and two consecutive readouts agree.
- The eye must be seated. Until terrain data loads, the virtual camera is not standing at its summit ("elev not set" in the engine's own log) and every probe reports sky.
- No journey in flight. Measuring the moment the panel wakes raced all 17 probes; the sweep demands the aim hold still first — the same precondition the auto-boresight uses.
- Tiles stream where you look. A fresh scene has no far ridge yet — the sweep pans the arc to force streaming, then re-probes three sentinels; if the scene shifted mid-measurement, it measures again.
- Near canopy is not far terrain. Where a forested near crest occludes the far ridge, the photo's skyline and the bare-earth model's disagree by whole degrees — those columns are culled, and candidates are scored with a trimmed mean so localized features (the one hump on a smooth ridge) keep their vote. A too-flat cost valley still refuses: a featureless skyline cannot pin a width, and the sweep says so on its progress card instead of storing noise.
Rungs are allowed to fail — the range's two ends are datasheet-anchored anyway, so a rejected z=1 or z=9999 costs nothing. Four good mid-range points outline the curve; the server re-checks physics (zoom ascending, FOV strictly descending, bounds) and the calibration agency allow-list before anything lands in the store.
Who does what
- The frontend renders and solves: it aims the engine from each image's stamped pose, runs the skyline auto-boresight, applies corrections, renders the facet ring, and owns Explore / Fine-tune / Locate.
- The BFF routes pass through to the backend and degrade gracefully when the backend is older than the frontend (an empty fleet store just means seeds carry on).
- The backend is the referee and the memory: it re-validates every solver gate, enforces source precedence and the calibration agency allow-list, stamps who submitted, and serves the whole fleet's boresights and measured zoom-optics curves in anonymous reads. Storage is one small document per camera (boresights) or per lens model (optics curves) in the existing camera container — no new infrastructure.
Rollout is three ops steps: the enable-peakfinder feature-flag row per
environment, aboveGroundLevel + panoZoom flowing once the API deploys,
and (optionally) seeding the store from the shipped seeds.
Where to go deeper
- Aligning PeakFinder to a Photograph — the engineer's reference: every error source ranked and measured, the SDK's render contract, and the pano geometry.
- 360 Stitching — why the strip is the shape it is.