A mobile-first shooting coach. Log a string, photograph the target, and BallisticLens reads the group, computes where the rounds should have landed, and measures the gap to where they actually did. The pipeline is real and built honest. The intelligence sitting on top of it — the diagnostic rule set, the coaching vocabulary — is early, and these diagrams say so.
A measured stream runs left to right: the target photo goes through Imagick hole-detection to a centroid — the real center of the group. A predicted stream runs underneath it: the build's zero, height-over-bore, and velocity go through the ballistic solver to a theoretical point of impact. The two converge on the Shooter Delta — the vector between where the rounds went and where they should have. Everything downstream is reading that one number.
All of this runs synchronously inside the save request — no queue, no worker, no cron. The merge is the structure worth remembering: a measured fact and a predicted fact meeting at a signed difference. The two boxes on the right are deliberately drawn in grey, not copper — they're the stages where the product is still thin.
Copper says where the round should have gone — the solver's prediction, dead center for a confirmed zero. Blue says where it actually went — the measured centroid of the holes in the paper. The coral vector between them is the entire value proposition. Today the engine names all eight directions of that gap; reading it across a whole string — cadence, fatigue, cold-bore drift — is the next frontier.
The color choice is the point: keeping predicted (copper) and measured (blue) distinct is what lets the page say the math said here, the rounds went there. Collapse them into one color and you lose the only sentence that matters.
This isn't a "no AI" flex; it's a right-tool-per-step story. Classical vision, then physics, then a rule engine, then a scored lookup, then a generative voice. The interesting and honest part is the fill of each bar — two stages carry the product, two are skeletons, and the voice is deliberately gated.
No LLM sits in the diagnostic path. The Anthropic key is wired into config as a placeholder for future generative coaching — it is not what produces today's read. Saying that plainly is the whole reason this chapter exists.
BallisticLens prefills an estimate for anything it can infer — height-over-bore, velocity, zero — and records how it got there. Leave it alone and it saves as estimated. Touch it and it becomes manual or measured, and the user's number always wins. Diagnostics soften their certainty when an input was only a guess.
This is the architecture's real backbone and the part that's genuinely thought-through: the system stores not just a number but its origin and its trust, so nothing downstream pretends to be more certain than the inputs deserve.
The session composites into a 1200×1600 range card with PHP-GD — copper hit rings, a blue centroid crosshair, and a stat block. It's the app's pure image-craft piece: zero AI, classical raster. And it's privacy-first by construction — initials only. Email, full name, original filename, GPS, and device metadata never reach the card.
The stat line — "Δ low-left · trigger anticipation" — prints for any group, naming the mechanical fault across all eight directions of the chart and pairing each with the drill that confirms it. It now also weighs confidence by group dispersion: a tight offset group earns a confident mechanical read, a scattered one gets told to fix the grip before chasing a direction. What's still ahead is reading the fault across a whole string — cadence, fatigue, cold-bore drift. Every verdict still ships with the ball-and-dummy caveat: a probable cause to confirm, not a sentence.