Projects across computer vision, market experiments, learning platforms, games, a family recipe
archive, and a couple of browser extensions. The highway danger-scoring system is the one I'm
proudest of.
Featured
Behavioral danger scoring from overhead highway video
Solo · June–July 2026 · ~25,000 lines of Python across ~125 modules · 257 commits · built in about three weeks
An end-to-end system that watches highway traffic from overhead video — drone footage or research
datasets — tracks every vehicle, and scores each driver's behavior in real time on a 0–100 Danger
Index. The goal is to surface the drivers genuinely endangering people around them (tailgaters,
weavers, aggressive cut-ins) rather than crude proxies like raw speed, and to do it in a way that
is validated against human judgment and explainable signal by signal.
The analyst view mid-run on real drone footage — every vehicle boxed and colored by its running
assessment, beside the ranked board of the worst drivers among 1,548 tracked, on a feed the
system self-calibrated from a cold start in 10 seconds.
01The problem
Traffic analytics today mostly measures crude proxies: spot speed, hard-braking counts,
loop-detector volumes. None of those capture what a traffic officer or safety engineer actually
means by "that driver is dangerous" — behavior relative to surrounding traffic. Following two car
lengths back at 70 mph. Forcing other drivers to brake. Threading across three lanes.
Overhead video can see all of it, with no instrumentation in the cars. The hard parts are turning
pixels into clean, physically-calibrated trajectories; turning trajectories into a defensible
danger measure; and proving that measure agrees with human judgment rather than just asserting it.
02Detection and tracking
A car in a 4K drone frame can be a few dozen pixels wide, and naive full-frame detection downscales
the image and erases exactly the vehicles you need. The detector slices each frame into overlapping
tiles at native resolution, runs a YOLOv8 network pretrained on the VisDrone aerial benchmark over
each tile, and merges results with class-aware non-maximum suppression plus a full-frame pass for
large vehicles straddling tile seams.
Off-the-shelf detectors see a semi as a flexing pair of cab and trailer boxes, which wrecks
tracking. I solved it geometrically first, then at the source — fine-tuning a dedicated whole-rig
truck detector using the geometric merger's output as training labels.
Tracking is formulated as a min-cost network-flow problem rather than greedy frame-to-frame
matching: every detection is a node, edges carry motion and appearance costs, and the solver finds
the globally optimal set of vehicle paths. Physical structure lives in the graph itself — vehicles
can't appear or vanish mid-road for free. The guiding principle throughout was to lose a track
cleanly rather than recover it wrongly; a dropped track costs coverage, but a wrong recovery
silently corrupts every safety metric downstream.
03Self-calibration
Scoring needs meters and m/s, but a drone feed arrives as pixels with unknown scale and
orientation. The system bootstraps its own geometry from the traffic it observes — scale estimated
from lane spacing against standard lane widths (2.3% median error across a 49-clip sweep), traffic
direction inferred from the detections themselves. A deliberate scoping call, informed by simulator
experiments: don't chase absolute speed accuracy from uncalibrated video, because the metrics that
matter — headways, time-to-collision — only need relative geometry to be right.
04The scoring engine
Ten signals built on surrogate safety measures from the traffic-safety literature — time headway,
time-to-collision, forcing others to brake, cutting in with insufficient gap, weaving, speed
against surrounding flow — each normalized 0–100 and individually explainable, computed over a
rolling three-second window.
Three design choices did most of the work. Aggregation is noisy-OR, not a weighted
average, because with an average one genuinely dangerous behavior gets diluted by the nine
that don't apply. Follower signals are gated on causal context — braking hard
because the car ahead braked is defense, not aggression, and this single idea eliminated the
largest class of false positives. And the index is a chronic disposition measure,
with a separate acute detector for conflict events graded by physical consequence.
Danger Index — thresholds anchored to human judgment, not to an alert budget
0Watch 50Critical 80100
Live mode on a 15-minute drone clip: raw feed, analyst view with score-colored boxes, the ranked
board of 1,737 tracked drivers, and the drill-down replay of one flagged vehicle — rebuilt from
retained footage so any driver's episode can be reviewed on demand.
05Proving it, rather than asserting it
The question disciplining everything above: does the score agree with what a reasonable person
calls dangerous? Raters watched pairs of vehicle clips and picked the more dangerous driver, blind
to the system's scores — a two-alternative forced choice, which measures the ranking the system
actually claims to produce without asking anyone to invent a numeric scale.
Result
Measure
~92%
Pooled human–system agreement on held-out research recordings never used during development
88.5%
Pooled agreement on real drone footage, through the full pipeline — detection, tracking and self-calibration included, not just the scorer on clean data
81%
Of held-out conflict events where the score was already elevated above that driver's own baseline beforehand. With all proximity information ablated, behavioral signals alone still anticipate ~65% — the score isn't merely re-detecting closeness
Negative results were kept. A time-headway rework that failed to improve human concordance was
shelved with its analysis written up rather than merged on vibes. One rater's disagreement traced
to a different rubric — they were scoring norm violations while the system scores risk creation —
and that was documented as early evidence for officer-selectable weight profiles, rather than
treated as noise.
06Performance
Every hot path was profiled before being touched, and every optimization landed only after an
equivalence check against the implementation it replaced.
Component
Result
Verification
Scoring engine
~2–4 ms/frame — real time at 25 fps with ~10× headroom
output-identical
Track association
25.9 → 6.9 ms/frame
bit-identical
Flow solve
23 s → 0.08 s per clip via OR-tools C++ solver
identical optimal cost
Detection stage
FP16 + TensorRT + auto-ROI + tile batching
bit-identical
Live assessment board
62 → 0.35 ms per update at 30k drivers
output-identical
Two results worth mentioning because they went the other way: GPU acceleration of the scorer was
evaluated and rejected — the workload is many small windows, not big tensors, so the win came from
prefix sums and algorithmic complexity instead. And warm-starting the flow solver was refuted by
profile: the C++ solve was only ~9% of step time, so the assumed bottleneck wasn't the bottleneck.
07What I'd fix next
A pooled 88.5% from seven raters over a two-dozen-pair deck is encouraging evidence, not proof. The
gaps I consider real, roughly in the order I'd attack them:
Validation that tests calibration, not just ordering. Forced-choice comparisons can show the score ranks drivers the way people do; they structurally cannot show that 80 is the right place for the Critical threshold.
Weight profiles instead of one universal rubric. The rater who disagreed most wasn't wrong — they were scoring norm violations while the system scores risk creation. Ablating the tailgating signal moved their agreement from 54% to 69% while collapsing another rater's from 100% to 45%.
A better reference for speed-vs-flow. Today the surrounding-flow reference is a narrow lateral band, so a speeder embedded in fast traffic drags the reference up and reads as compliant.
The stopped-vehicle blind spot. The filter that correctly ignores parked vehicles also drops a vehicle stopped in a live lane — precisely the event the stopped-in-lane signals exist to catch.
Geometry breadth. Everything validated so far is straight multi-lane highway. Curved and angled roads need an auto-fit road model that's specced but not built.
Every script registers in a point-and-click toolbox — the rule being that a tool isn't finished
until someone who isn't me can launch it. Here mid-launch of the timed live pipeline, with the log
showing calibration warm-up, TensorRT engines loading, and throughput ramping to ~28 fps.
Python · NumPy · OpenCV · PyTorch · Ultralytics YOLOv8 (VisDrone-pretrained + custom fine-tune) ·
TensorRT · Google OR-tools · SciPy · pandas · stdlib HTTP server + vanilla-JS web UIs ·
highD dataset · BeamNG.drive for simulated development footage
Provisional patent application drafted and prepared for filing — full specification, 20 claims, 10 figures.
Also
Other projects
2026 · Ongoing · Personal investing project
Silly Prices Holdings
Built on the old Buffett and Graham idea that quality companies sometimes trade at silly
prices. The plan: keep a pre-vetted watchlist of wonderful businesses, wait for a temporary
dislocation — Google has a bad month — buy at a real margin of safety, and hold indefinitely.
Nothing is bought to flip.
The tooling is a zero-dependency Python pipeline, standard library only, over free SEC EDGAR
fundamentals and Yahoo prices. It verifies the quality gates from primary SEC filings, builds
each company's own-history valuation bands, and auto-generates a public board that rates the
roughly 50 largest US-listed companies against a rule-based buy target — the 20th percentile
of each name's own EV/EBIT history, with separate lenses for financials, cyclicals, and
foreign ADRs.
The qualitative half is automated too. When a name enters the buy zone, Claude researches it
with web search into a structured verdict — temporary dislocation or permanent decline, with
a confidence and named disqualifiers. That runs as a daily cloud routine, and the board just
re-renders from the committed cache, so the verdicts are reproducible rather than one-off.
Python (standard library only) · SEC EDGAR XBRL · GitHub Actions · GitHub Pages · Claude
A personal, long-term investing project and my own methodology. Not investment advice.
WatchlistWonderful companies passing hard quality gates (Piotroski F ≥ 7, ROIC > WACC, Altman Z > 2.99), each with a written moat thesis, built in calm markets.
ScreenDaily flag of any name in the silly-price zone: down ≥ 20% and valued in the bottom ~15–20% of its own 10-year history.
DiagnoseTemporary dislocation or permanent decline? Moat intact and damage bounded, or a value trap? Only temporary proceeds.
ValueBuy only at a third or more below a conservative estimate of intrinsic value.
AcquireIn three tranches: initial entry, deeper once stabilized, and a final add once the thesis is confirmed.
HoldSell only on a broken thesis, a broken management integrity, or egregious overvaluation. Never on price alone.
2026 · Ongoing · Paper-traded research
Can reasoning catch a news story's second-order effect before the market does?
When a policy story breaks, the obvious names get repriced almost instantly. The knock-on
effects — whose costs quietly go up, whose competitor just got a gift, whose supply chain
tightened — seem to take longer for the market to work out. I don't know yet whether that
gap is real and catchable or whether I'm imagining it. This is my attempt to find out.
The idea: read a policy headline, reason one step past the obvious, name who might be on the
other side of the trade and why they're stuck — forced to act, slow to react, or trading on
emotion — then log a prediction with a date and a fixed exit and score it later. One example:
a scary chip-supply headline had people selling crypto miners; the thought going the other
way was that fewer imported rigs makes the network less competitive, which helps the machines
already running. Right or wrong, I have no idea yet. That's what the scoring is for.
The hard part isn't the ideas, it's not fooling myself. It's easy to remember the guesses
that worked and quietly forget the rest, so the point isn't to be right — it's to find out
whether the judgment is doing anything at all. The reasoning runs on an LLM, and it's built
on a whole-market intraday data pipeline (Databento pulls normalized into partitioned Parquet).
A personal research experiment and paper-trading exercise. Not investment advice, not a
service, and no real orders are placed.
Capture the newsPolicy and regulatory headlines, timestamped so there's a record of exactly what I saw and when.
Find the second-order effectThe fast money already owns the obvious trade. The interesting part is one step removed from the headline.
Guess who's mispricing itEach idea has to name who's on the other side and why they'd be stuck. Often I can't, and that's fine.
Write it down, let it play outLogged with a date and a fixed exit, then scored against what happened. Bad calls stay on the record next to the good ones.
Forward-only, never backtested. Rules fixed before the first prediction. Passed-on ideas
logged too. No claims until there's enough data — right now there isn't.
2026 · Solo · Full-stack web app
Mood Music
A Spotify companion that reframes your saved library as a story rather than a list. Its
Phases view reads your Liked Songs in the order you saved them and detects the eras your
taste has moved through — a mellow acoustic stretch, a high-energy run, a moodier recent
turn — each rendered as an expandable timeline you can page through track by track.
Its Mood & Activity search lets you ask your own library for a feeling or a moment —
"heartbreak," "late-night coding," "rainy Sunday" — and ranks your songs by how well they
match. Each track carries a mood score that blends its audio character with the sentiment of
its lyrics, placing it on a valence-and-energy map. The result knows not just what you listen
to, but how it feels and when it mattered.
TypeScript · Node.js / Express · React / Vite · Spotify Web API (OAuth 2.0) · ReccoBeats · LRCLIB · AI agent routines
Resilient to a shifting upstreamMid-build, Spotify deprecated its audio-analysis and genre endpoints for new apps. I re-architected the enrichment layer around alternative sources (ReccoBeats, LRCLIB) with graceful degradation, so the product survived a breaking change without losing its core idea.
Offline AI enrichmentThe semantic work — lyric sentiment, interpreting natural-language mood queries — runs as a batch agent routine that commits results to cache files the app reads instantly. No per-request LLM latency or cost; the hand-off is bridged through git.
Change-point detection for erasPhases come from binary change-point segmentation over a smoothed audio-feature trajectory (raw per-track signals were too noisy to segment directly), with each era labeled by how it deviates from the listener's own baseline.
Privacy by designSigned-cookie multi-user sessions; personal listening data never leaves the machine. Only an anonymized track manifest bridges to the enrichment routine, and lyrics are never stored — only the sentiment derived from them.
2026 · Solo
The Reinhardt Family Recipe Box
My grandmother has a recipe box our family has cooked out of for decades. There's only one
of it, so I scanned every card and put the whole thing online for everyone to use.
The site looks like the box: click a divider tab to open that category, or search across
all of it. It's one HTML file, a JSON manifest, and two folders of images — no server and
no database, so there's nothing to keep running.
A Python script handles the rest. It reads the original scans, makes web and thumbnail
copies, and rebuilds the manifest, pulling titles, categories, and multi-page recipes out
of the filenames. Adding a recipe means dropping a scan in a folder and running it again.
The originals never change.
HTML · CSS 3D transforms · vanilla JavaScript · Python · Pillow
Card scans, 215 recipes spanning more than one card
22
Categories, one divider tab each
1
HTML file — the entire application
0
Servers, databases, or dependencies to keep alive
2026 · Solo
YT Retitler
A browser extension that rewrites clickbait YouTube titles into honest ones, based on what the
video actually says. Every video in your feed gets a small button; click it and the title is
replaced with a plain description drawn from the video's own transcript — no withheld payoffs,
no manufactured urgency. Click again to revert.
The interesting part is the transcript. YouTube now gates caption access behind a proof-of-origin
token that browser requests can't produce, so the extension sidesteps it with a small local
helper that fetches the transcript on your own machine — keeping the whole flow private instead
of routing it through a third party. The transcript goes to an LLM under a structured-output
schema that forces a clean, prose-free title, which is written back in place preserving
YouTube's styling.
Runs entirely on your machine; API keys stored locally and never leave the device except to the
chosen model provider.
2022 · Solo
It Ends
A wall-jumping platformer built in Unity. It started as coursework and turned into something
rather larger than the assignment called for — the kind of project where the brief stops being
the point about a third of the way in.
A Blockly implementation for Cerebrum, Deepbrook's custom language for setting up environments
and context in their nurse-training game. It gave students an approachable way to work with the
language, and the transpiler ran both directions so existing Cerebrum files became editable
workspaces instead of being rewritten by hand.
A plugin for logging student actions during gameplay to a Moodle LMS. Before it, every
SCORM/xAPI request was hard-coded into the Unity project; afterwards a developer could generate
a log for any event without touching the transport layer.
An investment scenario simulator that used machine learning to produce possible projections of
company performance. I proposed it and managed the development across eight months. It does not
produce accurate projections for stock values — but it was a good experiment in whether that
particular approach was valid at all, and the answer to that is worth having.
A Chrome extension I wrote to fit my own needs: it injects a filter control into Twitch's
followed-channels page so you can show only channels playing selected games, or hide them.
Shipping it was also how I learned what it takes to get listed in a standardized marketplace.