This content originally appeared on DEV Community and was authored by Ibrahim Pima
How I’m Building a Racing-Analysis Web App from Raw Telemetry
(And How You Can Copy-Paste the Whole Stack)
0. The Spark
I spent last winter watching GT4 cars throw 32768-lap grenades into their data streams while the real lap count quietly hid in the time stamps. The ECU clock drifted like a cheap Rolex, but the GPS trace never lied.
That mess is now becoming a Next.js app that turns any $200 OBD+GPS logger into a pro-level race-eng tool. Below is the exact blueprint I’m coding to, parameter by parameter.
1. Data Model – One JSON per Lap
Every time the car crosses the start/finish line I collapse the last chunk of rows into a single document that lands in MongoDB (Atlas free tier).
Shape:
{
_id: ObjectId,
car: { chassis: “004”, sticker: “78”, ecuTimeOffsetMs: -2473 },
session: { track: “Sebring-12”, date: “2025-11-22”, type: “Race” },
lap: { nr: 14, time: 123.456, dist: 3861.2, valid: true },
telemetry: {
speed: [96,97,98,…], // 20 Hz
gear: [4,4,4,…],
rpm: [5200,5300,…],
throttle: [78,79,…],
brakeF: [0,0,12.3,…],
brakeR: [0,0,8.7,…],
accX: [-0.12,-0.11,…],
accY: [0.44,0.46,…],
steer: [2.1,2.3,…],
lat: [40.1234567,…],
lon: [-74.654321,…],
sDist: [0,1.6,3.2,…] // distance from SF line (m)
},
meta: {
sampleRate: 20,
ecuClockDriftMs: -2473,
sourceFiles: [“GR86-004-78_20251122_Race.log”]
}
}
Notice I store arrays, not rows – one lap = one document = lightning-fast reads.
2. Lap Count Fix – Kill 32768
Pseudo-code (runs in Node API route):
const FIX_LAP = (rows) => {
let lap = 0, lastDist = 0, lastTime = 0;
return rows.map(r => {
if (r.lap === 32768 || r.lap < lap) {
// derive lap from distance
if (r.Laptrigger_lapdist_dls < lastDist) lap++;
r.lap = lap;
} else {
lap = r.lap;
}
lastDist = r.Laptrigger_lapdist_dls;
lastTime = r.timestamp;
return r;
});
};
3. ECU Clock Drift Compensation
Grab the first GPS UTC timestamp and the ECU timestamp for the same sample:
driftMs = GPS_UTC – ECU_time;
Store driftMs in the car document; subtract it on every future render so engineers see real wall-clock time.
4. Frontend Stack – Next.js 14 + Tailwind + Recharts
Pages
/ – upload .log or .csv
/[chassis] – list all sessions
/[chassis]/[id] – single lap analyser
Components
– two laps, same trace, delta colour-map
– brake pressure overlay, automatic threshold detection
– Mapbox GL, paint racing line by speed (turbo colour scale)
– scatter accX vs accY, 50 ms dots, convex-hull “g-g” envelope
All graphs are SVG, <20 kB each – works on a pit-lane iPad over 4G.
5. The Magic – Automatic Brake Point Detection
- Compute speed derivative (m/s²).
- Find first sample where decel > 6 m/s² and brakeF > 5 bar.
- Walk backward until speed derivative < 1 m/s² → that’s lift-off.
- Store distance from SF line.
Now you can sort every lap by “brake point T1” and instantly see who’s late.
6. Delta-T Computer – No VBOX Pro Needed
Because we have Laptrigger_lapdist_dls at 20 Hz, we build a reference lap (best valid lap) then for any other lap:
delta[i] = (t_ref[sDist[i]] – t_lap[i]) * 1000 // ms
Paint it as a colour band under the map trace – green = faster, red = slower.
7. Corner Names Without a Track Map
K-means cluster (lat, lon) samples where |accY| > 1.2 g.
Each cluster centre = apex.
Let the user label once (“T1”, “T2”) → store in a tiny JSON config per track.
Next session the app auto-tags corners.
8. Deployment – Free for Club Racers
Frontend: Vercel hobby tier (git push → live in 30 s).
Backend: Next.js API routes + MongoDB Atlas (512 MB free).
File storage: Vercel blob (upload logs up to 100 MB, auto-deleted after 30 days).
Auth: GitHub OAuth – no passwords, just your team.
9. Copy-Paste Roadmap
Week 1
– Scaffold Next.js 14, /api/upload, parse CSV → raw JSON.
– Build , show raw parameters in a table.
Week 2
– Implement lap fix & drift compensation.
– Store one-lap documents in MongoDB.
– Build table (lap time, tyre, fuel).
Week 3
– Mapbox overlay, colour by speed.
– Brake-point detection API, render as flags on map.
Week 4
– Delta-T trace, two-lap overlay.
– Export best lap as CSV (for engineers who still love Excel).
Week 5
– Corner naming UI, accY cluster.
– Dark mode, print CSS so drivers can tape the page to the dash.
10. What I Learned So Far
- 32768 is the new 404.
- GPS distance beats ECU lap count every single time.
- Storing arrays instead of rows shrinks bundle size by 70 %.
- Brake pressure > 5 bar is the simplest “am I braking?” gate across every GT car I’ve tested.
- Club racers will give you beer if you auto-detect their brake points – pro teams will give you money if you can do it for 30 cars in real time. Guess which feature I’m building next…
This content originally appeared on DEV Community and was authored by Ibrahim Pima