BINX FORGEALL YOU NEED IS AN IDEA.Support BINX ↗
REIMAGINE A GAME · CHECKED 2026-10-11

Mario Kart 64 PC Port: SpaghettiKart Explained, Then Build Your Own Racer

Discover Harbour Masters’ SpaghettiKart PC port, its maintainers and native rendering, then build an original drift-and-boost kart time trial with free source, assets and AI.

What the creator made

Mario racing on Luigi Raceway in Mario Kart 64 — actual original-game screenshot, not a verified PC-port capture
Actual Mario Kart 64 gameplay screenshot. Original game imagery; not a verified capture of SpaghettiKart. Game imagery © Nintendo. Source: VICE, Mario Kart 64 retrospective ↗. Shown for editorial review; not included in the reusable tutorial assets. No affiliation or endorsement is implied.

The race view, item slot and lap HUD illustrate the kart-racing systems discussed here.

SpaghettiKart

Harbour Masters — maintainers MegaMech, Coco and Kirito, with the credited contributors

SpaghettiKart brings Mario Kart 64 to modern platforms through a decompilation-based port. The official README names MegaMech, Coco and Kirito as maintainers and links the Harbour Masters website and project documentation.

The project uses libultraship, supports several graphics backends and lets users supply compatible custom asset archives. Its release notes describe interpolation, custom-track and CPU-racer improvements. The release checked here is 1.0.0.

SpaghettiKart excludes commercial game assets and requires the user’s supported game copy. Its source path differs from Mario Kart 64 static-recompilation projects: similar game names do not identify the same team or technique.

Latest published release checked: 1.0.0, 25 February 2026 (UTC) · checked 2026-10-11.

AI involvement is not established in the checked README or release. A decompilation project is not automatically an AI remake.

See the creator’s own release and documentation for project media. Our Stormwheel images are labelled concept art or actual independent tutorial renders; they are not SpaghettiKart screenshots.

How it works: a decompilation-based native port

The port builds game source with a modern platform layer and native rendering. Assets are extracted into an O2R archive from the supported game copy; DirectX 11, OpenGL and Metal serve different desktop targets. This is distinct from directly translating an N64 executable with a static recompiler.

  1. Recovered game source
  2. Modern platform library
  3. Extracted assets from a supported copy
  4. Native rendering and controls

libultraship ↗ · SpaghettiKart build documentation ↗ · Modding documentation ↗

Recompilation translates instructions; decompilation aims to recover higher-level structure; a mechanics recreation writes a new implementation. An AI tool can help with inspection or coding, but those terms alone do not establish AI use.

Public repository: no repository-wide licence was found in the checked root. Do not treat all game source as reusable open-source tutorial code; dependencies have their own notices.. Read the original licence ↗. Open-source tool licensing does not license the commercial game’s art, audio, characters or binaries. Forge supplies no ROMs or commercial game downloads.

Learn reverse engineering using permitted practice code → · Explore AI game mashups and creator examples →

LOVE THIS GAME? BUILD YOUR OWN.

What could your game look like?

Stormwheel Rally · arcade kart racer

Electric courier karts compete around copper weather towers and coastal roads. Orange bodywork, turquoise charge trails and original helmeted drivers create a distinct racing identity.

Original Stormwheel Rally concept art: electric karts race through a stormy seaside circuit; not Mario Kart gameplay
Original AI-generated concept art. Proposed art direction, not creator gameplay or the playable reference.

Drive two clockwise laps before the timer expires. Learn the road, cross gates in order, charge a drift and release it for a short boost.

The complete MIT reference is a 2D top-down time trial: one geometric oval, one kart, acceleration/braking/steering, drift-release boost, a turbo pickup, ordered lap gates, a 90-second timer, pause and restart. Rivals, weapons, 3D art, wet-road physics, saving and the illustrated seaside track are proposed expansions.

Stormwheel Rally actual geometric track at the start
Actual original renderer on native Canvas with fresh state; not a browser or creator screenshot.
The original kart has passed an ordered checkpoint
Actual simulation and renderer after scripted steering. The seaside concept art is a separate proposed direction.
The original kart turns while its drift charge increases
Actual simulation state and renderer; native Canvas output does not verify keyboard or touch input.

Build your own game: thirteen visual steps

Create your own original game inspired by these gameplay ideas. This tutorial does not reproduce the featured commercial game or imply affiliation with its creators.

  1. Choose the free racing engine

    Start with the complete dependency-free JavaScript time trial. It proves kart rules without installing an editor. For a fuller 3D racer, Godot is free under MIT; install an exact version and use matching official vehicle/physics documentation. The supplied browser source is not a Godot-native project. VehicleBody3D uses raycast wheels and has documented limitations; assess it for an arcade test before committing to advanced vehicle physics.

    Actual Forge Build a Game genre choices
    Genuine existing Forge capture, 10 October 2026. Choose the genre; it does not show the creator’s game.
  2. Set up the complete course

    Download the ZIP, extract the stormwheel folder and serve all nine files over HTTP. With Python 3, run python -m http.server 8080 inside it and open /index.html. ES modules require a JavaScript MIME type. Run node check.mjs before making changes.

  3. Select compatible free resources

    Use the geometric reference first. Kenney Racing Kit supplies 3D kart/track models and City Kit Roads supplies optional road pieces. They do not supply physics, AI, laps or a complete track importer. For a 3D adaptation, import one model at a time, check units, forward axis and collision separately. UI and Impact Sounds are optional CC0 ingredients.

  4. Create one controllable kart

    newState() creates position, heading, speed and one timer owner. Accelerate increases speed, Brake decreases it, and steering scales with speed. Do not teleport a car to simulate motion. The original code bounds the road test and elapsed-time substeps. In a 3D editor, separate visual wheels, collision and controller.

  5. Build a small readable circuit

    The first track is an oval with inner/outer road limits and four gates. Keep the start clearly visible. Add only one curve or shortcut at a time; every legal route must cross the same ordered gates. Original art can use a seaside road later, while track collision stays separate from decoration.

  6. Implement steering, drift and camera

    The reference is top-down with a fixed camera so steering and track edges are visible. Toggle Drift while turning above the speed threshold; charge grows within a cap. Releasing Drift consumes charge once for a short boost. A chase camera is a separate 3D expansion requiring obstruction tests and speed-readable framing.

    The original kart has passed an ordered checkpoint
    Actual simulation and renderer after scripted steering. The seaside concept art is a separate proposed direction.
  7. Count valid laps and finish

    acceptGate() rejects the wrong index or reverse crossing. The next gate wraps in order; crossing the finish after all gates increments one lap. Two accepted laps end the run. A turbo pickup has a cooldown, so one overlap cannot award continuous boosts. Test reverse driving and resetting before adding race rewards.

  8. Add rivals only after the time trial

    The supplied build contains no AI racers or weapons. Add a small waypoint follower as the intermediate upgrade; reuse a compatible engine system first. Keep rival count bounded, add visible recovery and require the same lap gates. Test corners and overtakes before making AI faster.

  9. Keep menus and mobile controls clear

    Start a new run, Pause/Resume, Toggle Drift and Reset to last gate are real DOM buttons. Arrows/WASD and touch hold directions feed the same rules. Each touch pointer owns its direction; releasing one cannot clear another. Leaving the tab/window pauses and clears held input. Verify actual controls on the published desktop and phone.

  10. Add animation, UI and effects

    The renderer draws an original kart, track and next gate from state. Show turning wheels, drift charge and boost using capped effects; do not change hitboxes from art. Add licensed UI/audio only after verifying import details, user activation and volume controls. The reference has no supplied soundtrack or audio player.

  11. Plan progression and saving carefully

    The first reference deliberately has no persistent save. A later time-trial record needs version, track ID, finite bounded duration and completion proof from accepted gates. Reject corrupt or incompatible records; blocked storage must not stop a race. Editable local records are not online leaderboards.

  12. Run complete race and boundary tests

    Run node check.mjs: it drives two actual laps at 15/30/60/120 updates, rejects skipped/reverse gates, checks drift release, reset, invalid time, pause/outcomes and timer loss. Exact simulation checks differ from physical keyboard, touch, browser rendering or frame-time measurements. Test those separately on the published target.

  13. Publish, then expand one system

    Publish all nine files on a static HTTPS host. Check file paths/MIME, first load, two valid laps, timeout, tab pause, restart and mobile hold/cancel. Then choose one upgrade prompt. AI rivals, 3D tracks and a campaign in the full prompt are planned milestones, not features claimed in the geometric reference.

Complete original code and download

Download complete MIT source ZIP · Run, test and publish instructions · MIT licence

index.html — complete sourceOpen this file
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content="Original free kart racing tutorial: a two-lap time trial with steering, drift, boost and ordered gates."><title>Original game Stormwheel Rally · BINX Forge</title><link rel="stylesheet" href="game.css"><script type="module" src="main.mjs"></script></head>
<body><main><a id="guide" href="https://binxforge.com/guides/mario-kart-64-spaghettikart-build-original-racer">← Follow the BINX Forge guide</a><p class="eyebrow">ORIGINAL MIT KART TUTORIAL · 1.0</p><h1 id="name">Stormwheel Rally</h1>
<label for="lesson">Tutorial example</label><select id="lesson"><option value="kart">Stormwheel Rally · kart racing</option></select>
<p id="instructions">Enable JavaScript and start a small game. Source, licence and instructions remain available.</p><div class="hud"><p>Progress: <output id="score">0</output></p><p>Time: <output id="time">90</output></p></div>
<canvas id="game" width="640" height="360" tabindex="0" aria-label="Original game example area" aria-describedby="instructions status">This visual example requires Canvas and JavaScript. Read the guide and source for text instructions.</canvas>
<p id="status" role="status">Ready. Start a new run.</p><div class="actions"><button id="start" disabled>Start a new run</button><button id="pause" disabled>Pause</button></div>
<div class="actions"><button id="action0" disabled>Toggle drift</button><button id="action1" disabled>Reset to last gate</button><button id="action2" hidden disabled>Third action</button></div>
<div class="directions" role="group" aria-label="Movement controls"><button data-direction="up" aria-label="Up / accelerate">↑</button><button data-direction="left" aria-label="Left">←</button><button data-direction="down" aria-label="Down / brake">↓</button><button data-direction="right" aria-label="Right">→</button></div>
<p class="note">Focus the canvas for arrows or W/A/S/D, or hold the direction buttons. Actions also work with keyboard-focused buttons. Leaving the tab pauses and clears input. Resume is explicit. Examples have no accounts, network play, saving, third-party art or sound. Their 2D rules are a learning milestone; native input, phone and assistive-technology acceptance remains to be checked.</p>
<p><a href="stormwheel.zip">Download all complete files</a> · <a href="README.md">Run and test instructions</a> · <a href="LICENSE.txt">Original MIT source licence</a></p><noscript><p>Enable JavaScript to play. Read or download the complete source without an account.</p></noscript></main></body></html>
main.mjs — complete sourceOpen this file
/* One frame owner · reuses Pocket Engine input/lifecycle/timing patterns · MIT. */
import {newState,advance,action,modes,lessons} from './worlds.mjs';
import {render} from './render.mjs';
import {createInput} from './input.mjs';
export function createController(doc,win){
 const byId=id=>doc.getElementById(id),canvas=byId('game'),ctx=canvas.getContext('2d');
 const requested=new URLSearchParams(win.location.search).get('mode');let mode=modes.includes(requested)?requested:'kart',state=newState(mode),previous=null,frameId=null,pausedFrom='running';
 const input=createInput(doc,win,canvas,pause);
 function sync(){
  const l=lessons[mode];byId('name').textContent=l.name;byId('instructions').textContent=l.instructions;
  byId('guide').href='https://binxforge.com/guides/'+l.guide;byId('lesson').value=mode;
  if(byId('status').textContent!==state.message)byId('status').textContent=state.message;
  byId('score').textContent=state.mode==='creatures'?state.party.length:state.score;
  byId('time').textContent=state.remaining===undefined?'—':Math.ceil(state.remaining);
  byId('pause').disabled=!['running','battle','upgrade','paused'].includes(state.status);byId('pause').textContent=state.status==='paused'?'Resume':'Pause';
  l.buttons.forEach((label,i)=>{const b=byId('action'+i);b.textContent=label;b.hidden=!label;b.disabled=state.mode==='creatures'?state.status!=='battle':state.mode==='survivors'?state.status!=='upgrade':state.status!=='running';if(state.mode==='kart'&&i===0)b.setAttribute('aria-pressed',String(state.drift));else b.removeAttribute('aria-pressed');});
 }
 function pause(){input.clear();if(['running','battle','upgrade'].includes(state.status)){pausedFrom=state.status;state.status='paused';state.message='Paused. Resume when ready.';}previous=null;sync();}
 function draw(){render(ctx,state);sync();}
 function loop(now){frameId=null;if(!doc.hidden&&previous!==null)advance(state,input.read(),(now-previous)/1000);previous=now;draw();frameId=win.requestAnimationFrame(loop);}
 function schedule(){if(frameId===null)frameId=win.requestAnimationFrame(loop);}
 byId('start').addEventListener('click',()=>{input.clear();state=newState(mode);state.status='running';state.message=lessons[mode].instructions;previous=null;canvas.focus();draw();});
 byId('pause').addEventListener('click',()=>{if(state.status==='paused'&&!doc.hidden){state.status=pausedFrom;state.message='Resumed.';previous=null;canvas.focus();sync();}else pause();});
 byId('lesson').addEventListener('change',()=>{const value=byId('lesson').value;if(!modes.includes(value))return;input.clear();mode=value;state=newState(mode);previous=null;draw();});
 for(let i=0;i<3;i++)byId('action'+i).addEventListener('click',()=>{action(state,i);input.clear();canvas.focus();draw();});
 win.addEventListener('pagehide',()=>{pause();if(frameId!==null)win.cancelAnimationFrame(frameId);frameId=null;});
 win.addEventListener('pageshow',()=>{previous=null;schedule();});
 byId('start').disabled=false;draw();schedule();return Object.freeze({getState:()=>state});
}
if(typeof document!=='undefined')createController(document,window);
input.mjs — complete sourceOpen this file
/* Pocket Engine1.0 · adapted from BINX Forge Coin Dash1.0 · MIT. */
const directions={ArrowUp:'up',ArrowDown:'down',ArrowLeft:'left',ArrowRight:'right',w:'up',s:'down',a:'left',d:'right'};
export function createInput(doc,win,canvas,pause) {
  const keys=new Set(),pointers=new Map(),listeners=[];
  function listen(target,name,handler){target.addEventListener(name,handler);listeners.push(()=>target.removeEventListener(name,handler));}
  const inside=element=>element===canvas||element?.matches?.('[data-direction]');
  const direction=key=>directions[key.length===1?key.toLowerCase():key];
  function clear(){keys.clear();pointers.clear();}
  function leave(){clear();pause();}
  listen(doc,'keydown',event=>{const d=direction(event.key);if(!d||!inside(doc.activeElement))return;event.preventDefault();keys.add(d);});
  listen(doc,'keyup',event=>{const d=direction(event.key);if(d)keys.delete(d);});
  for(const button of doc.querySelectorAll('[data-direction]')){
    listen(button,'pointerdown',event=>{if(event.pointerType==='mouse'&&event.button!==0)return;button.setPointerCapture(event.pointerId);pointers.set(event.pointerId,button.dataset.direction);});
    for(const name of ['pointerup','pointercancel','lostpointercapture'])listen(button,name,event=>pointers.delete(event.pointerId));
  }
  listen(doc,'focusout',event=>{if(inside(event.target))keys.clear();});
  listen(win,'blur',leave);listen(doc,'visibilitychange',()=>{if(doc.hidden)leave();});
  return Object.freeze({read:()=>Object.fromEntries([...keys,...pointers.values()].map(d=>[d,true])),clear,destroy(){clear();for(const remove of listeners)remove();}});
}
worlds.mjs — complete sourceOpen this file
/* Stormwheel Rally 1.0 — original independent MIT tutorial, adapted from BINX Forge Genre Lab's public kart rules. */
export const W=640,H=360;
const clamp=(n,a,b)=>Math.max(a,Math.min(b,n));
const distance=(a,b)=>Math.hypot(a.x-b.x,a.y-b.y);
export const modes=['kart'];
export const lessons={kart:{name:'Stormwheel Rally',guide:'mario-kart-64-spaghettikart-build-original-racer',instructions:'Finish two clockwise laps in 90 seconds. Up accelerates, down brakes, left/right steer. Toggle Drift while turning; release it for a boost. The cyan pickup supplies turbo.',buttons:['Toggle drift','Reset to last gate','']}};
export function newState(mode='kart'){if(mode!=='kart')throw Error('Choose the kart lesson.');return {mode,status:'ready',age:0,message:'Ready. Start a new run.',player:{x:560,y:180},score:0,angle:Math.PI/2,speed:0,remaining:90,nextGate:1,laps:0,drift:false,charge:0,boost:0,pickupCooldown:0,lastGate:0};}
export const gates=[{x:560,y:180},{x:320,y:300},{x:80,y:180},{x:320,y:60}];
export function acceptGate(s,index,forward){
 if(s.status!=='running'||s.mode!=='kart'||!forward||index!==s.nextGate)return false;
 s.lastGate=index;s.nextGate=(index+1)%4;
 if(index===0){s.laps++;s.score=s.laps;if(s.laps===2){s.status='won';s.message='Two laps complete! Start a new run.';}}
 return true;
}
function kartStep(s,input,dt){
 s.remaining=Math.max(0,s.remaining-dt);s.boost=Math.max(0,s.boost-dt);s.pickupCooldown=Math.max(0,s.pickupCooldown-dt);
 s.speed=clamp(s.speed+((input.up?105:0)-(input.down?200:0)-(!input.up&&!input.down?45:0))*dt,0,s.boost>0?240:170);
 const steering=Number(!!input.right)-Number(!!input.left);
 s.angle+=steering*1.8*(s.speed/170)*(s.drift?1.3:1)*dt;
 if(s.drift&&steering&&s.speed>60)s.charge=Math.min(1.5,s.charge+dt);
 const before={...s.player};s.player.x+=Math.cos(s.angle)*s.speed*dt;s.player.y+=Math.sin(s.angle)*s.speed*dt;
 const radius=Math.hypot((s.player.x-320)/240,(s.player.y-180)/120);
 if(radius<0.70||radius>1.30){s.player=before;s.speed*=0.4;s.charge=0;s.message='Track edge: steer back onto the road.';}
 const gate=gates[s.nextGate],tangents=[{x:0,y:1},{x:-1,y:0},{x:0,y:-1},{x:1,y:0}],t=tangents[s.nextGate];
 const prevSide=(before.x-gate.x)*t.x+(before.y-gate.y)*t.y,side=(s.player.x-gate.x)*t.x+(s.player.y-gate.y)*t.y;
 if(prevSide<0&&side>=0&&distance(s.player,gate)<65)acceptGate(s,s.nextGate,true);
 if(s.pickupCooldown===0&&distance(s.player,{x:320,y:60})<22){s.boost=1;s.pickupCooldown=8;s.message='Turbo pickup: one second of boost.';}
 if(s.remaining===0&&s.status==='running'){s.status='lost';s.message='Time is up. Start a new run.';}
}
export function step(s,input={},dt){if(s.status!=='running'||!Number.isFinite(dt)||dt<=0||dt>0.01+1e-9)return;s.age+=dt;kartStep(s,input,dt);}
export function advance(s,input,elapsed){
 if(!Number.isFinite(elapsed)||elapsed<=0)return;const time=Math.min(0.1,elapsed),count=Math.ceil(time/0.01);for(let i=0;i<count;i++)step(s,input,time/count);
}
export function action(s,index){
 if(s.mode==='kart'&&s.status==='running'){
  if(index===0){s.drift=!s.drift;if(!s.drift){if(s.charge>=0.6){s.boost=0.8;s.message='Drift release: boost!';}s.charge=0;}}
  if(index===1){s.player={...gates[s.lastGate]};s.angle=[Math.PI/2,Math.PI,-Math.PI/2,0][s.lastGate];s.speed=0;s.charge=0;s.drift=false;s.message='Reset to last valid gate; progress preserved.';}return;
 }
}
render.mjs — complete sourceOpen this file
/* Original geometric top-down kart render; not a commercial game screenshot. MIT. */
import {W,H,gates,lessons} from './worlds.mjs';
function circle(c,x,y,r,colour){c.fillStyle=colour;c.beginPath();c.arc(x,y,r,0,Math.PI*2);c.fill();}
function text(c,value,x,y,size=16,colour='#f3f6fa'){c.fillStyle=colour;c.font=size+'px system-ui';c.fillText(value,x,y);}
export function render(c,s){c.clearRect(0,0,W,H);c.fillStyle='#14293a';c.fillRect(0,0,W,H);
  c.strokeStyle='#486173';c.lineWidth=70;c.beginPath();c.ellipse(320,180,240,120,0,0,Math.PI*2);c.stroke();
  for(let i=0;i<4;i++){circle(c,gates[i].x,gates[i].y,12,i===s.nextGate?'#ffe58d':'#93aabb');text(c,i===0?'Finish':String(i),gates[i].x-18,gates[i].y-24,14);}
  if(s.pickupCooldown===0)circle(c,320,60,8,'#82d7ff');
  c.save();c.translate(s.player.x,s.player.y);c.rotate(s.angle);c.fillStyle=s.boost>0?'#82d7ff':'#ffae80';c.fillRect(-14,-9,28,18);c.fillStyle='#102033';c.fillRect(-9,-13,7,5);c.fillRect(4,-13,7,5);c.fillRect(-9,8,7,5);c.fillRect(4,8,7,5);c.restore();
  text(c,'Lap '+s.laps+'/2 · Next gate '+s.nextGate,196,168);text(c,'Drift '+(s.drift?'ON':'OFF')+' · Charge '+s.charge.toFixed(1),196,193,14);

 if(['ready','paused','won','lost','upgrade'].includes(s.status)){c.fillStyle='#071625e8';c.fillRect(100,130,440,90);text(c,s.status==='ready'?lessons[s.mode].name:s.status==='upgrade'?'Choose your upgrade below':s.status.toUpperCase(),164,168,23);text(c,['won','lost'].includes(s.status)?'Start a new run to play again':s.status==='paused'?'Use Resume to continue':'See instructions and controls below',164,199,14);}
}
game.css — complete sourceOpen this file
:root { color-scheme: dark; font-family: system-ui, sans-serif; background: #101c2b; color: #f3f6fa; }
* { box-sizing: border-box; }
body { margin: 0; padding: 20px; }
main { max-width: 720px; margin: auto; }
a { color: #9be2ff; }
h1 { margin: .4em 0; }
p { line-height: 1.6; }
.eyebrow { color: #8cd8e9; font-size: .75rem; letter-spacing: .1em; }
.hud, .actions { display: flex; flex-wrap: wrap; gap: 16px; }
.hud { justify-content: space-between; }
.hud p { margin: 8px 0; }
canvas { display: block; width: 100%; height: auto; border: 2px solid #53788c; border-radius: 12px; background: #14293a; }
button { min-width: 64px; min-height: 48px; padding: 10px 16px; border: 2px solid #8cd8e9; border-radius: 8px; background: #163b50; color: #fff; font: inherit; cursor: pointer; }
button:disabled { opacity: .55; cursor: default; }
:focus-visible { outline: 3px solid #ffde80; outline-offset: 3px; }
.directions { display: grid; grid-template-columns: repeat(3, 64px); gap: 8px; justify-content: center; margin-top: 20px; }
.directions button { touch-action: none; user-select: none; }
[data-direction="up"] { grid-column: 2; }
[data-direction="left"] { grid-column: 1; grid-row: 2; }
[data-direction="down"] { grid-column: 2; grid-row: 2; }
[data-direction="right"] { grid-column: 3; grid-row: 2; }
.note { color: #b8c9d6; font-size: .875rem; }

select{display:block;max-width:100%;min-height:48px;background:#163b50;color:#fff;border:2px solid #8cd8e9;font:inherit;padding:8px;border-radius:8px}.actions{margin:14px 0}[hidden]{display:none!important}
README.md — complete sourceOpen this file
# Stormwheel Rally 1.0

An independent MIT 2D top-down kart time trial. Adapted from the existing public BINX Forge Genre Lab kart rules; no commercial game code, characters or assets.

Extract all nine files in the stormwheel folder. Serve over HTTP using python -m http.server 8080 and open /index.html; .mjs must use JavaScript MIME. Run node check.mjs (Node 24.19.0). No external library or asset purchase is required.

Up/W accelerates, Down/S brakes, Left/A and Right/D steer. Toggle Drift while turning and release after charging to boost. Cyan turbo pickup refreshes after eight seconds. Cross all four gates clockwise for two laps before 90 seconds. Reset restores the last accepted gate without awarding progress. Pause/resume and restart use the same one-loop controller.

This reference contains one geometric oval, one kart and a timer. AI rivals, weapon pickups, saving, 3D models, wet-road physics, campaign and the generated seaside concept artwork are proposed upgrades, not current features. Tab/window leave pauses; touch uses multiple pointer IDs. Browser/phone/audio/clipboard acceptance is separate from simulation tests.

Publish all files on a static HTTPS host preserving relative paths. Test first load, controls, direction, gates, win/timeout, focus, two restarts and phone layout on the actual target. Source notices stay with every redistributed copy.
LICENSE.txt — complete sourceOpen this file
MIT License

Copyright (c) 2026 BINX Forge

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
check.mjs — complete sourceOpen this file
import assert from 'node:assert/strict';
import {newState,advance,action,acceptGate,gates} from './worlds.mjs';
let n=0;const check=(name,fn)=>{fn();n++;console.log('PASS '+name);};
function drive(rate){const s=newState();s.status='running';for(let i=0;i<rate*90&&s.status==='running';i++){const a=Math.atan2((s.player.y-180)/120,(s.player.x-320)/240)+.24,tx=320+240*Math.cos(a),ty=180+120*Math.sin(a),d=Math.atan2(ty-s.player.y,tx-s.player.x),e=Math.atan2(Math.sin(d-s.angle),Math.cos(d-s.angle));advance(s,{up:true,left:e<-.04,right:e>.04},1/rate);}return s;}
check('complete real two-lap route at 15/30/60/120 update rates',()=>{for(const r of [15,30,60,120]){const s=drive(r);assert.equal(s.status,'won');assert.equal(s.laps,2);assert.ok(s.remaining>0);}});
check('ordered forward gates reject skipped, reverse and duplicate crossings',()=>{const s=newState();s.status='running';assert.equal(acceptGate(s,0,true),false);assert.equal(acceptGate(s,1,false),false);assert.equal(acceptGate(s,1,true),true);assert.equal(acceptGate(s,1,true),false);assert.equal(s.nextGate,2);assert.equal(s.laps,0);});
check('drift charge is bounded and release awards one boost',()=>{const s=newState();s.status='running';s.speed=100;action(s,0);advance(s,{up:true,right:true},.1);assert.ok(s.charge>0);s.charge=1;action(s,0);assert.equal(s.boost,.8);assert.equal(s.charge,0);advance(s,{},.1);assert.ok(s.boost<.8);});
check('reset preserves accepted gate and cannot award a lap',()=>{const s=newState();s.status='running';acceptGate(s,1,true);s.player.x=12;action(s,1);assert.deepEqual(s.player,gates[1]);assert.equal(s.nextGate,2);assert.equal(s.laps,0);assert.equal(s.speed,0);});
check('invalid time freezes state and long debt is capped',()=>{const s=newState();s.status='running';const before=JSON.stringify(s);for(const t of [NaN,Infinity,0,-1])advance(s,{up:true},t);assert.equal(JSON.stringify(s),before);advance(s,{up:true},100);assert.ok(s.age<=.100001);});
check('pause and both outcomes freeze; runs are independent',()=>{for(const status of ['ready','paused','won','lost']){const s=newState();s.status=status;const before=JSON.stringify(s);advance(s,{up:true,right:true},.1);action(s,0);assert.equal(JSON.stringify(s),before);}const a=newState(),b=newState();a.player.x=0;assert.equal(b.player.x,560);});
check('timer produces loss while outside-track movement rolls back',()=>{const s=newState();s.status='running';s.remaining=.001;advance(s,{},.01);assert.equal(s.status,'lost');const a=newState();a.status='running';a.angle=0;a.speed=170;a.player.x=632;const before={...a.player};advance(a,{up:true},.01);assert.deepEqual(a.player,before);assert.ok(a.speed<100);});
console.log(n+' kart rule checks passed; browser/phone acceptance is separate.');

Free vs paid resources

Free firstWhat it contributesLicence / compatibility
Godot
Godot Engine contributors
Free, open-source game editor for 2D and 3D projects, with scene, animation and interface tools.MIT · Creator source ↗
Import one compatible file; art is separate from rules.
Racing Kit
Kenney
Track and vehicle artwork for arcade racing scenes.CC0-1.0 · Creator source ↗
Import one compatible file; art is separate from rules.
City Kit (Roads)
Kenney
Road assets for city layouts and driving prototypes.CC0-1.0 · Creator source ↗
Import one compatible file; art is separate from rules.
UI Pack
Kenney
Interface artwork for buttons, panels and sliders.CC0-1.0 · Creator source ↗
Import one compatible file; art is separate from rules.
Impact Sounds
Kenney
Short impacts and footsteps for action feedback and surface changes.CC0-1.0 · Creator source ↗
Import one compatible file; art is separate from rules.

Free source code, artwork, engine licences and commercial game files are separate. Check permissions to sell a finished game, resell files and include them in source/template products. An AI opinion is not licence clearance.

YOUR ORIGINAL GAME

COPY TO MAKE GAME

Paste the complete development brief into your AI. It includes the working source, verified resource facts, milestones and target tests. Review it and adapt the identity to your own idea.

Select and copy the text manually if clipboard access is unavailable.

Review the complete game-making prompt
Customise in the existing game builder

Full article, prompts and source as AI-readable Markdown →

MAKE YOUR GAME EVEN BETTER

Beginner: Readable racing HUD and wheel animation

UI Pack · Impact Sounds

Review or select the upgrade text.

Intermediate: Three fair AI rivals and a second track

Godot · Racing Kit · City Kit (Roads)

Review or select the upgrade text.

Advanced: A tested 3D racer and asynchronous ghosts

Godot · Racing Kit · UI Pack

Review or select the upgrade text.

Questions before you start

Is this an AI Mario Kart 64 remake or a commercial game download?

No. The original project is a separately credited PC port. Our example and AI prompt create Stormwheel Rally, an independent game with its own identity.

Can ChatGPT, Claude or Codex build the full game?

Use an AI tool that can edit and run your chosen project, work through the milestones and review its output. A copied prompt alone is not a tested finished game. Follow the source and licence checks even when the AI sounds confident.

Why free code before paid assets?

A working controller, outcome and restart reveal what the game actually needs. Buy an optional resource only when its files, compatibility and rights solve that need.

More games to build

Sources and creator credit

Project facts checked 2026-10-11. Resource editions carry their own checked dates. No measured search volume, ranking promise or independent AI-output acceptance is claimed. Harbour Masters — maintainers MegaMech, Coco and Kirito, with the credited contributors owns the featured project; no endorsement is implied. Corrections and review policy ↗