An advanced, web-based lap timing and telemetry system designed for RC (Radio-Controlled) cars. The system uses client-side computer vision (via OpenCV.js) to detect cars using color profiles and shape contour matching as they cross a user-defined finish line zone.
I tested the system on a Carrera Digital 1:24 circuit allowing lane changing. During my tests, it worked quite well with yellow, blue, red or green cars. The detection was succesful around 99.9% of the time according to my estimations. I however noticed a a few missed detection for a F1-shaped grey car depending on the ambiant luminosity and the speed of the car when passing the finish line (~97% success).
The application has mostly been developed using Google Antigravity AI Coding. Feel free to reuse it or modify it for your won purposes.
The application utilizes a split architecture where the backend purely serves the static resources, and the client browser runs the entire computer vision and audio logic in real-time.
graph TD
subgraph Browser ["Web Browser (Client Side)"]
User[User Interface / DOM]
AppJS[app.js - State, Audio & UI Controller]
CVEngine[cv-engine.js - Computer Vision Engine]
OpenCV[OpenCV.js Library]
LocalStorage[(Browser LocalStorage)]
Webcam[Webcam Video Stream]
Webcam -->|Frame Capture| CVEngine
OpenCV <-->|Image Processing| CVEngine
CVEngine -->|Detections, Score, Contours| AppJS
AppJS -->|Render Overlay & Lap Stats| User
AppJS -->|Beeps & Announcements| AudioContext[Web Audio API]
AppJS <-->|Save Setup, HoF & Stats| LocalStorage
end
subgraph Server ["Backend Server (Python)"]
MainPy[main.py - FastAPI Server]
end
MainPy -->|Serves files over HTTP| User
Here is a visual walkthrough of the key sections of the user interface:
The control center of the lap timer. It allows camera selection, drawing finish and detection areas, scanning new cars, registering drivers, setting target lap counts, and adjusting threshold settings.

The active race view. Features live lap timers, current session standings, and real-time text logs. The webcam display can be shown or hidden dynamically to save screen space.

Tracks lifetime stats for each driver, including win counts, total laps completed, total track time, average lap time, and car usage telemetry (color-coded breakdown of driving sessions per car).

The record leaderboard detailing the fastest lap times ever achieved on this system, categorized by driver and car with exact date-time stamps.

To run this application successfully, your environment must meet the following requirements:
- Python 3.8+: Required only to run the local web server hosting the assets.
- Modern Web Browser: A browser supporting:
- HTML5 Camera API (
navigator.mediaDevices.getUserMedia) - Web Audio API (
AudioContext) - Canvas with
willReadFrequentlyoptimization - Google Chrome, Microsoft Edge, Firefox, or Safari are recommended.
- HTML5 Camera API (
- Web Camera: A USB webcam or integrated camera capable of streaming at 640x480 resolution (ideal).
- Good Track Lighting: HSV (Hue, Saturation, Value) color detection relies heavily on consistent lighting. Avoid shadows or direct, changing glare on the finish line area.
- Distinctively Colored RC Cars: Cars should be painted in solid, distinct colors (e.g., bright blue, red, neon green, yellow) that differ significantly from the color of the track surface.
- Possibility to place the camera with a top-down view: the camera should be looking at the finish line with a top-down view (camera above the track, 90+-15 degrees angle to the track).
Follow these steps to run the application on your local machine:
-
Navigate to the Project Directory:
cd RC_LapTimer_ColorCars_V1 -
Set Up a Virtual Environment (Optional but recommended):
- Windows (PowerShell):
python -m venv venv .\venv\Scripts\Activate.ps1 - macOS/Linux:
python -m venv venv source venv/bin/activate
- Windows (PowerShell):
-
Install Dependencies:
pip install -r requirements.txt
-
Start the FastAPI Web Server:
python main.py
The console will display:
Serving on http://localhost:8000. -
Access the App: Open your browser and navigate to http://localhost:8000.
[!IMPORTANT] Ensure you grant the browser permission to access your webcam when prompted.
The application features a granular configuration dashboard. Understanding how these settings impact detection helps prevent false triggers and missed laps.
| Setting | UI Element | What It Does / Impact |
|---|---|---|
| Camera Selector | Dropdown | Allows selection of the connected video source. Saves selection in localStorage for ease of use in future sessions. |
| Draw Finish Area | Button | Lets you click and drag a cyan bounding box (FINISH ZONE) over the video stream. Impact: Cars are only timed if their bounding box center falls inside this box. Keep this area narrow to pinpoint crossing times. |
| Draw Detection Area | Button | Lets you click and drag an orange dashed box (DETECTION AREA). Impact: Restricts OpenCV processing to this box only. Highly recommended to crop out spectators, non-track movements, and reduce CPU usage. |
| Capture Empty Track | Button | Captures a baseline reference image of the track. Impact: Mandatory first step for scanning cars using difference thresholding. |
| Place Car & Scan | Button | Compares the current webcam frame with the empty track baseline to find differences and extract the car's HSV range, contour shape (reference points), and area. |
| Target Laps | Input | Sets the target lap count for a race. Impact: If set to > 0, the first driver to reach this lap count ends the race and triggers a text-to-speech announcement. Set to 0 for infinite training/practice sessions. |
| Lap Cooldown | Input (Seconds) | Sets the minimum time window required between consecutive lap crossings for a single car. Impact: Prevents double-counting a single crossing if the car moves slowly or gets stuck. Displays Cooldown Active! on screen if triggered too early. |
| Shape Tolerance | Slider (0.1 - 2.0) | Adjusts the strictness of the contour template match (Hu Moments). Impact: Lower values (e.g. 0.2) require an exact match to the scanned car profile. Higher values (e.g. 1.0) allow looser matching. Turn down if cars are mismatching; turn up if detections are dropping due to camera blur. |
| Motion Filter | Checkbox | Activates background subtraction (BackgroundSubtractorMOG2). Impact: Ignores stationary objects, meaning a car must be actively moving to trigger a lap. Extremely useful to filter out stationary spectators or track borders of similar colors. |
| Save Current Setup | Button | Saves drivers, scanned cars, detection zones, cooldowns, and driver-to-car slots to the browserβs localStorage. |
All image processing runs in real-time inside the browser window using OpenCV.js (loaded asynchronously via the official CDN).
[Camera Frame] ββ> [Crop to Detection Area] ββ> [Convert RGBA to HSV]
β
[Contour Bounding Box] <ββ [Dilate & Find Contours] <ββ [HSV Color + Motion Mask]
β
βββ> Aspect Ratio Check (Filter out long, thin shadows)
βββ> Solidity Check (Filter out sparse pixel noise)
βββ> Shape Matching (Hu Moments match against the scanned profile)
- Frame Capture and Cropping: Every frame from the camera is copied onto a canvas. If a Detection Area is defined, the CV engine crops the processing frame to these coordinates, discarding the rest of the image to optimize processing speed.
- Color Conversion: The frame is converted from RGBA to RGB, and then to HSV (Hue, Saturation, Value). HSV is ideal for color tracking as it separates color info (Hue) from lighting intensity (Value).
-
Background Subtraction (Motion Filtering):
If the motion filter is enabled, OpenCV's
BackgroundSubtractorMOG2computes a running foreground mask of moving objects. A strict binary thresholding operation is applied to isolate solid moving bodies from subtle shadows. -
Color Segmenting (HSV Masking):
The engine checks if the scanned car is chromatic (has color) or achromatic (white, grey, black):
-
Chromatic: Isolates a narrow Hue band (e.g., Β±15 Hue units to prevent overlap) and enforces a minimum saturation of
40to avoid matching grey tracks. Supports Hue wrapping (e.g. Red wrapping from 180 to 0). - Achromatic: Ignores Hue, creating a mask based on the Saturation and Value ranges of the car.
-
Chromatic: Isolates a narrow Hue band (e.g., Β±15 Hue units to prevent overlap) and enforces a minimum saturation of
-
Mask Logic:
The color mask and the motion mask are combined using
cv.bitwise_and. The resulting mask represents pixels that are both moving and match the car's color. -
Contour Extraction & Noise Filtering:
The combined mask undergoes dilation (expanding pixels with a 5x5 kernel to fill holes) and is analyzed via
cv.findContours. Blobs smaller than500pixels are discarded. -
Shape Verification & Filtering:
-
Aspect Ratio: Rejects contours where
$\frac{\text{width}}{\text{height}} > 5$ or$\frac{\text{height}}{\text{width}} > 5$ (typically long, thin shadows). -
Solidity: Rejects blobs with a solidity (Contour Area / Bounding Box Area)
$< 0.15$ (typically sparse noise or scattering leaves). -
Contour Match (Hu Moments): Compares the detected blob's shape against the scanned reference shape using
cv.matchShapes. The score combines the shape matching discrepancy and the area ratio discrepancy:$$\text{Score} = \text{Shape Penalty} + 0.2 \times (\text{Area Ratio} - 1)$$ The candidate contour with the lowest score under the user's Shape Tolerance is selected.
-
Aspect Ratio: Rejects contours where
Once a car is successfully detected, the frontend controller (app.js) handles session tracking:
- Overlap Resolution: If multiple scanned car profiles match the same physical coordinate box on screen, they are sorted by their shape matching score. The best match is assigned to that physical object, preventing a single car from triggering multiple driver lap slots.
- Finish Zone Crossing: The geometric center of the detected bounding box
(cx, cy)is compared against the boundary coordinates of the user-drawn Finish Zone. - Waiting-for-Exit Guard: At the start of a race/training session, any car sitting stationary inside the Finish Zone is flagged as
waitingForExit. Its lap timer will not start until it completely drives out of the Finish Zone. This prevents false lap records when cars line up on the grid. - Synthesized Audio Feedback: Uses the Web Audio API to generate driver-distinct pitches.
- Driver 1:
600Hz - Driver 2:
800Hz - Driver 3:
1000Hz - Countdown sequence: Triggers
440Hzbeeps on count 3, 2, and 1, followed by a longer880Hztone on "GO!".
- Driver 1:
RC_LapTimer_ColorCars_V1/
β
βββ main.py # FastAPI server script (serves frontend over HTTP)
βββ requirements.txt # List of Python dependencies
βββ README.md # Documentation file (this file)
β
βββ static/ # Directory containing client-side assets
βββ index.html # Main application page (contains UI layout)
βββ style.css # UI Styling (Dark mode, glassmorphism, responsive tables)
βββ app.js # Application state, timer loop, database, and UI logic
βββ cv-engine.js # Low-level OpenCV.js webcam and image processing engine
The application runs locally and respects your privacy. All telemetry, leaderboards, and configurations are saved directly to your browser's persistent database (LocalStorage):
rc_drivers: Registered driver profiles.rc_preset: Complete setup presets including custom boundary zones and session slots.rc_hall_of_fame: Lifetime record timings (driver name, car, lap time, and date).rc_driver_stats: Lifetime tracking data, including total lap count, active drive time, wins, and car usage telemetry (rendered as badge achievements).