A retro pixel-art top-down driving game, fully self-contained in a single HTML file. Designed to run in any modern browser — on desktop and mobile.
You control a car driving down a multi-lane road and must avoid oncoming traffic for as long as possible. Score comes from PASSING cars — not from survival time itself — multiplied by a score multiplier you raise with fewer lanes, higher difficulty, a bigger car, and above all speed. Scattering an obstacle's cones and being in the next lane when two cars wreck each other both pay on top. Settings let you tune difficulty. In-game, the TUTORIAL button explains all of this with the real numbers.
Two ways, both offline — the fonts are embedded in the file, so nothing is fetched from the network.
As a Windows app. Run build\RecklessDrivingSetup.exe. It installs for your account only
(%LOCALAPPDATA%\Programs\RecklessDriving), needs no administrator rights, and adds a Start menu and
desktop shortcut. Windows SmartScreen will warn the first time because the exe is unsigned — "More info"
→ "Run anyway". To update, run a newer RecklessDrivingSetup.exe: it spots the existing install and the
button says Update. Your scores, coins, cars and achievements live in %LOCALAPPDATA%\RecklessDriving,
outside the program folder, so an update never touches them. Uninstall from Settings → Apps (saves are
kept unless you tick the box).
Android: install the APK from the latest release (see Updating).
From the main menu: START RUN to play, SETUP for settings, SCORES for the local leaderboard, TUTORIAL for the in-game HOW TO PLAY screen (shown automatically on a first launch).
| Action | Desktop | Mobile |
|---|---|---|
| Steer left/right | Arrow keys or A/D (snaps to the nearest lane) | On-screen arrows or stick (Settings → Touch Controls) |
| Move forward/back | Up/Down arrow keys | The same arrows, or push the stick up/down — the stick does both axes at once |
| Use ability | Hold Spacebar | Hold the USE button |
| Pause | P, Escape, or the pause icon (top-left) | Tap the pause icon |
Dragging on the road and tilting the phone were both removed in v3.4.x: dragging put your hand over the road you were reading and offered no way to accelerate or brake, and tilting was never accurate enough to steer with.
| Setting | Options | What it does |
|---|---|---|
| Control Type | PC / Mobile | PC: keyboard only, no on-screen controls. Mobile: on-screen controls |
| Multi-Lane Hold | On / Off | Off = one lane per press or tap; On = hold to glide across several. On Mobile it lives on the Controls screen |
| Controls (Mobile) | EDIT → its own screen | Every touch option - arrows or joystick, size, height, style, layout, joystick position - with a live preview of your screen. Tap the preview to see it full size |
| Lane Change Speed (Mobile) | 1-5 | How fast the car crosses lanes while a direction or a diagonal is held. 1 is the original speed, 5 is 1.5x |
| Tap Road For Ability (Mobile) | On / Off | No ability button: touch the road - hold to keep jumping, tap to ram. Not with the floating joystick |
| Volumes | Master, plus music / siren / explosion / SFX | — |
| Skip Crash Animation | On / Off | Straight to the summary |
| Fullscreen | On / Off | Hides the phone's navigation bar while you play |
| Score Popups | On / Off | Floating +points when you pass or jump over a car |
| Text Size | Normal / Large / Larger | 100 / 115 / 130% on every screen's scrolling list. On a phone, small labels already have an ~11px floor |
| FPS Counter | On / Off | Bottom-right of the game screen |
| Abbreviate Money | On / Off | Shows 50K / 10.8M instead of the full number |
| Speed-Scaled Music | On / Off | Tempo rises as you speed up |
| Check For Updates | On / Off | See Updating |
| Transfer Save (OTHER) | OPEN | Move your progress to another device - see Moving your save |
Road lanes, difficulty and car colour are not here — lanes and difficulty are chosen on the main menu (both are remembered, like every setting) (they drive the score multiplier: 3 lanes x1.10, 4 lanes x1.00, down to 10 x0.82 - traffic per lane is the same at every count, so a wider road is more forgiving and pays less), and paint is in the Garage.
Lanes on a phone: every lane count from 4 up shows lanes at the same size and the same distance ahead. A road wider than the screen slides sideways to follow your car, and warnings for lanes off the screen appear at its edge with an arrow. A PC always shows the whole road.
Speed always accelerates and semi trucks are always on — no longer configurable. The ability you get is decided by your CAR, not a setting: Jump (43 cars), Shield Bump ram (8 heavy cars), or the Tank's ranged shot. Difficulty multiplies score too (SAFE x0.75, RECKLESS x1.00, SUICIDAL x1.25), and every km/h above 50 adds +1%.
Hold to fly and become immune to traffic (and ambulances) while airborne; release to land early. Needs at least 1 of the bar's 9 cells to take off (a single cell is a short hop), then drains for as long as it's held (about 3 seconds from full). Energy does not regenerate passively — it is earned back by passing cars (+1 cell, trucks +2, a grounded ambulance +5) and by close calls (+4).
Settings → OTHER → TRANSFER SAVE. Windows and Android apps only (a browser has no fixed device ID).
- On the new device, open it and note THIS DEVICE'S ID (e.g.
GMRT-7ZE2-VJFY). - On the old device, type that ID twice and press EXPORT, then choose where to save the file. This is a move: once the file is saved and checked, the old device starts over.
- Get the file to the new device (USB, cloud drive, a messenger attachment) and press IMPORT FILE there. It shows what the file holds and asks before replacing everything.
The file opens only on the device it was made for, only once, and not if it has been changed. Progress moves; settings stay on each device. If the file is lost before it is imported, that progress is gone - keep it until the import is done.
The game checks GitHub for a newer release on startup and whenever you come back to it (switching back to the app, or restoring the window - at most every 5 minutes), and shows a banner on the main menu when it finds one. Closing the banner hides it until the game next starts (coming back from another app does not bring it back). The check is a Settings toggle (CHECK FOR UPDATES, on by default) and there is a CHECK NOW button beside it. A failed check is silent unless you asked for it - it is usually just no internet.
| Where | What UPDATE does |
|---|---|
| Windows app | Downloads RecklessDrivingSetup-v<version>.exe (0-100% shown) and runs it; the app closes so the installer can replace it, and deletes the downloaded installer at its next start. Per-user, so no admin prompt. |
| Android | Downloads the APK (0-100% on the banner and in Settings) and hands it to Android's installer. Android always asks you to confirm - a sideloaded app may never replace itself silently. |
| Browser | Opens the release page, since there is nothing to install. |
If an update fails, the game says why, and UPDATE turns into TRY AGAIN with a GITHUB button next to it (the release page). Nothing opens by itself.
On Android the game needs "allow Reckless Driving to install unknown apps", once per phone
(REQUEST_INSTALL_PACKAGES). The first UPDATE opens that settings screen; coming back from it carries on
with the install by itself. The downloaded file (in the game's cache) is deleted once the new version runs.
Updates only install over a build signed with the same key. Since 3.25.0 the APK is signed with the project's own release key (see Android release key below). Installs from before 3.25.0 were debug-signed and cannot update to it in place: uninstall the old app first - which deletes the progress saved on that phone - then install the new APK.
python -m venv .venv
.venv\Scripts\python.exe -m pip install pywebview pyinstaller pillow
.\scripts\build.ps1Produces build\RecklessDrivingSetup.exe (~23 MB). The build refuses to run if app\version.py
disagrees with the game's own GAME_VERSION, or if carCrash.html still references Google Fonts, and it
runs the built exe hidden first to check the game boots, the fonts resolve offline and localStorage
is writable — a build that fails any of those is never packaged. -NoSelfTest skips that last step.
The window opens on a launch screen (app/splash.html — the logo on the game's dark background)
that hands over to the game. It is generated by tools/make_app_art.py; rerun that after changing
the launch artwork.
carCrash.html is wrapped for Android with Capacitor. The game is still a
single file — mobile/sync-web.js copies it into mobile/www/index.html at build time, and that copy is
build output, gitignored, never edited by hand.
Run npm install in mobile\ once, then build both the installer and the APK with one command:
.\scripts\build.ps1 -AndroidIt ends by stating what it produced, e.g. BUILT 3.5.1 - RecklessDrivingSetup.exe, RecklessDriving.apk.
Both land in build\; the APK is also left at mobile\android\app\build\outputs\apk\release\app-release.apk.
Without -Android only the installer is built, and any APK left in build\ by an earlier run is deleted
so it cannot be mistaken for part of the current build.
To build the APK on its own:
cd mobile
npm run sync
cd android
.\gradlew.bat assembleReleaseThe copy step repeats the Windows build's font guard: it refuses to run if carCrash.html still
references Google Fonts.
Requires the Android SDK, a JDK 21 and Node. On the current build machine all three are installed but
none are on PATH, so build.ps1 sets these itself when the environment does not already define
them — an environment that does define them wins:
$env:JAVA_HOME = "C:\Program Files\Android\Android Studio\jbr"
$env:ANDROID_HOME = "C:\Users\adam\AppData\Local\Android\Sdk"mobile\android\local.properties holds the SDK path and is gitignored, being machine-specific — Gradle
regenerates it, or write sdk.dir=<path> by hand.
The APK is signed with the project's own release key, and it installs by sideloading (allow "install unknown apps" on the phone). The key is never in this repository - the repository is public. It lives in the build machine's user profile:
| File | What it is |
|---|---|
%USERPROFILE%\.keystores\recklessdriving-release.jks |
The keystore (RSA 4096, alias recklessdriving, valid until 2054) |
%USERPROFILE%\.keystores\recklessdriving-signing.properties |
Its path and password, read by mobile\android\app\build.gradle |
build.ps1 -Android stops with an error if the properties file is missing, rather than producing an
unsigned or debug-signed APK.
Back both files up somewhere safe. Android only installs an update over an app signed with the same key, so if this key is lost, no installed copy can ever be updated again - every player would have to uninstall, and lose their progress, a second time.
Development happens in a Coder workspace. Two servers run from ~/game/:
| Port | What |
|---|---|
| 8080 | Live preview — open /carCrash.html, refresh after each edit |
| 8090 | Drag-and-drop upload page (chat attachments reject .html) |
Start them if they're not running:
cd ~/game && nohup python3 -m http.server 8080 > server.log 2>&1 &
cd ~/game && nohup python3 tools/upload_server.py > upload.log 2>&1 &
The project is a git repo — each working state is committed, so bad edits can be rolled back.
| File | Description |
|---|---|
carCrash.html |
The game (all HTML, CSS, JS in one file) - kept locally, not published in this repository |
app/ |
The Windows app host — main.py (WebView2 window), version.py, and the generated splash.html launch screen |
installer/ |
game.spec (PyInstaller) and setup.py (the install/update/uninstall window) |
scripts/build.ps1 |
Builds build\RecklessDrivingSetup.exe |
mobile/ |
Capacitor wrapper that packages the game as an Android APK |
tools/ |
embed_fonts.py (inline the fonts), make_app_art.py (Android icons, and both launch screens), make_icon.py (icon from the game's own sprite) |
assets/ |
gt-impact.ico — the app icon; loading-impact-transparent.png — the launch-screen art |
PLAN.md |
Development roadmap and feature backlog |
CHANGELOG.md |
Version history with dates and descriptions |
README.md |
This file |
See PLAN.md for the full roadmap. Highlights:
- Cosmetic trail "effects" (fire, smoke, stars) usable on any car
- Crosswalks and median/traffic-island obstacles
- Main menu redesign (per-tab icons, lanes/difficulty moved under START RUN)
- Music: per-biome track selection, more drum variety
- An online leaderboard, and an Android build
(The road obstacles, car shop, NPC visual variety and crash animation listed here previously are all
built — see CHANGELOG.md. Road pickups were tried and deliberately removed.)
- Rendering: HTML5 Canvas, internal resolution scales with lane count (
26px × lanes + 36) × 260px, displayed via a JS-computed exact-fit frame that preserves the true aspect ratio at any viewport size. On touch screens the zoom is fixed (4 lanes' fit) and a wider world is panned under the frame with a CSS transform, following the car - Pixel art drawn procedurally — no image assets. Road/verge scenery is pre-rendered into offscreen buffers once per lane-count change and blitted each frame
- Fonts: 'Silkscreen' (headers), 'VT323' (numbers) and 'DotGothic16' (body), embedded in the file as
base64
@font-face(latin + latin-ext subsets, ~68 KB) rather than fetched from Google Fonts, so the game keeps its type offline. Regenerate withpython tools\embed_fonts.py - Local leaderboard (top 10) persists via
localStorage, filterable by lane count - Target: Chrome, Firefox, Safari (iOS), Chrome (Android). Mobile is no longer treated as a prototype: v3.4.0 added real on-screen controls and safe-area handling, and v3.5.0 ships this same HTML as an Android APK through Capacitor rather than the separate native app once planned
All rights reserved — © 2026 Husarp. The code is public to read, and the releases are free to download and play, but the game may not be copied, changed, shared, sold or built upon without written permission. The embedded fonts (Silkscreen, VT323, DotGothic16, Jersey 10) keep their own licence, the SIL Open Font License. Full terms in LICENSE.