The entire game — HTML, CSS, and JavaScript — lives in one file. Understanding the internal layout of that file is the key to making any modification, whether you want to tweak a level theme, adjust movement speed, or wire up a new power-up.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/mbeckham4-hub/Rudi-Foodi/llms.txt
Use this file to discover all available pages before exploring further.
File Overview
| File | Purpose |
|---|---|
rudi_low_poly_3_d_game 6.html | The entire game (HTML + CSS + JS) |
preview.html | Static landing/preview page |
README.md | Project overview and feature list |
Internal Layout of the Game File
rudi_low_poly_3_d_game 6.html is organised into four sequential sections inside a single HTML document.
1. <style> Block
All visual styling lives in one <style> tag in the <head>. It covers:
- HUD (
#hud) — level counter, treat score, zoomies percentage, zoom-stacks button, clone-count button, spin-toggle button, power-up counter - Title screen (
#titleScreen,#gameTitle,#playButton,#controlsCard,#titleRudi) — the gradient splash screen shown before play starts - Joystick (
#joystickBase,#joystickKnob) — the left-side touch joystick - Zoom controls (
#zoomButton) — the ZOOMIES toggle button - Camera zone (
#cameraZone) — invisible full-screen overlay that captures drag-to-rotate pointer events - Overlay panels (
#creditsPanel,#leaderboardPanel,#nameEntryPanel) — modal cards for credits, the global/local leaderboard, and the post-game name-entry form - End screen (
#endText,#endingMessage,#goBackButton) — the ending overlay shown after fly-away or meteor ending cutscenes - Flash overlay (
#explosionOverlay) — a full-screen white div that fades in during the meteor explosion
2. <body> HTML — Static DOM Elements
The <body> declares the game’s UI skeleton before any JavaScript runs:
#titleScreen— full-screen title with the#playButtonPlay button, the animated Rudi preview canvas (#titleRudi), and the controls reference card#creditsPanel— modal overlay with credits and lore text#leaderboardPanel— modal overlay with the<ol id="leaderboardList">populated at runtime#nameEntryPanel— modal overlay with#usernameInput,#nameEntryError, Save and Skip buttons#hud— always-visible in-game heads-up display#hint— bottom-center hint bar updated dynamically during gameplay#cameraZone— transparent pointer-capture layer for camera drag#joystickBase/#joystickKnob— touch joystick#zoomButton— the ZOOMIES toggle button#explosionOverlay— white flash div#endText/#endingMessage/#goBackButton— end-game screen
3. Three.js CDN Import
Three.js is loaded from unpkg immediately before the main game script:4. <script> Game Logic
Everything else — scene setup, constants, Rudi, levels, audio, input, gameplay, endings, leaderboard, and the animation loop — is in one <script> block that begins with "use strict";.
Script Section Breakdown
All state is global within the script block. There are no ES modules, no classes, and no closures wrapping the top-level state. This is intentional — the codebase is designed to be readable and hackable without a module system.
Scene Setup
The Three.js world is created at the top of the script, before any functions are defined:THREE.Scenewith a sky-blue background (0x98dfff) and distance fogTHREE.WebGLRendererwithantialias: true, PCF soft shadows, and a pixel-ratio cap of2THREE.PerspectiveCamera(60° FOV) positioned behind and above the originhemiLight— aTHREE.HemisphereLightfor ambient sky/ground fillsun— aTHREE.DirectionalLightwith a1024×1024shadow map
Constants and State
Global variables declared immediately after the DOM references include:| Variable | Purpose |
|---|---|
gameStarted | Whether the title screen has been dismissed |
currentLevel | Active level number (1–20) |
score | Treats collected in the current level |
boost | Zoomies energy (0–100) |
speedStacks | Number of permanent speed power-ups collected |
speedMultiplier | Computed speed multiplier from stacks and level progression |
clones[] | Array of active Rudi clone THREE.Group objects |
treats[] | Array of active treat meshes |
powerUps[] | Array of active power-up orb meshes |
debrisObjects[] | Explosion debris meshes created by the meteor ending |
roomObjects[] | All meshes added by makeRoom(), cleared on level change |
Geometry Builders
Three small helper functions build all in-game geometry:mat(color, roughness)— returns aTHREE.MeshStandardMaterialwith flat shading disabled and minimal metalnessbox(w, h, d, color)— returns a shadow-castingTHREE.MeshwithBoxGeometrycone(r, h, color, sides)— returns a shadow-castingTHREE.MeshwithConeGeometrymakeRudi()— assembles a full RudiTHREE.Groupfrom boxes and cones: body, chest, head, snout, nose, face stripe, ears, tail, legs, eyes, and pupils. TheuserData.kindtag on each part lets the animation loop find legs, ears, tail, and head by traversal.
Level Data
levelThemes[]— 20 entries, one per level. Each entry defines floor colour, wall colour, sky colour, wood colour, dark colour, amapkey (e.g."house","maze","lava","city"), and asunboolean.musicThemes[]— 10 entries used byapplyLevelMusic(). Each entry contains chord frequency arrays and aspeedvalue (milliseconds per beat). Levels cycle through the 10 themes across all 20 levels.
Level Generation
makeRoom(level)— clearsroomObjects[], sets the scene background and fog to the level theme’s sky colour, recolours the ground and carpet, then builds level geometry based on the theme’smapkey (walls, islands, buildings, platforms, crystals, etc.).spawnLevel(level)— callsmakeRoom(), then clears and respawns treats and power-ups. Treat count scales with level (capped at 90); power-up count decreases with level (minimum 6).makeTreat()— creates a spinning gold cone treat and adds it to the scene andtreats[].makePowerUp(color, type)— creates a sphere orb with auserData.typestring ("speed","big","small","clone3","clone1") and adds it topowerUps[].
Audio
Theaudio object holds the Web Audio API context and node references. Key functions:
initAudio()— createsAudioContext,masterGain, andmusicGain. Called once on the first pointer interaction.beep(freq, duration, type, volume)— plays a one-shot sound effect using a temporary oscillator and gain envelope. Used for treat collection, jump, zoomies toggle, and level transitions.applyLevelMusic(level)— selects amusicThemesentry by(level - 1) % 10, then drives two oscillators (padOsc1,padOsc2) and a bass oscillator through chord and melody intervals usingsetInterval.stopMusic()— clears the music intervals and fades the music gain to near-zero.
Input Handling
keysobject — populated bykeydown/keyuplisteners. WASD and arrow keys map to movement.joyobject — tracks joystick pointer state (x,y,active,id). Pointer events on#joystickBaseupdate the knob position andjoy.x/joy.y.camTouchobject — tracks camera-drag pointer state.pointermoveon#cameraZoneincrementscameraYawandcameraPitch, which are clamped and used each frame to orbit the camera around Rudi.
Game Logic
updateGameplay(dt)— the main per-frame function during normal play. Reads keyboard and joystick input, converts to world-space movement, appliesvelocitywith friction, clampsrudiPosto the world bounds, animates Rudi’s legs/tail/ears/head, manages boost drain and recharge, checks treat and power-up collisions, updates the ending star, and positions the camera behind Rudi.updateClones(dt)— orbits each clone around Rudi at a radius based on clone count, checks clone-treat and clone-power-up collisions.updateDebris()— advances each debris object’s position by its velocity vector and applies a simple gravity decrement each frame.collectTreat(treat, collectedByClone)— increments score, plays a beep, respawns the treat at a random position, and triggers level advancement when the treat goal is met. Activates spin mode at 10 treats and dance mode at 20.applyPowerUp(type)— applies the power-up effect:"speed"incrementsspeedStacks;"big"/"small"setsizeMultiplierand schedule a reset;"clone3"callscreateClone()three times;"clone1"calls it once.
Endings
Two distinct ending sequences replaceupdateGameplay in the animation loop:
triggerFlyAwayEnding()/updateFlyAwayEnding(dt)— Rudi floats upward and flies off screen. Triggered when the last level’s treat goal is met after completing all 20 levels.triggerMeteorEnding()/updateMeteorEnding(dt)— a textured lava meteor descends from above and explodes, shattering all room geometry into debris. Triggered by collecting the golden ending star.advanceLevelAfterMeteor()— after the explosion settles, resets the scene and advances to the next level (or callsshowEndingif already on level 20).showEnding(message, completedGame)— displays#endTextwith the ending message and optionally callsaskForLeaderboardName().
Leaderboard
openLeaderboard()— callsrenderLeaderboard()and shows#leaderboardPanel.saveLeaderboard(entries)— writes the sorted entries array tolocalStorageunderrudiFoodiLeaderboardV1.askForLeaderboardName(timeMs)— shows#nameEntryPanelwith the player’s completion time and waits for a username submission. Validates against a banned-word list before saving.
GLOBAL_LEADERBOARD_ENABLED is true, reads and writes go to Firestore instead of localStorage.
Animation Loop
animate() uses THREE.Clock for frame-delta and caps dt at 0.033 seconds to prevent physics tunnelling on slow frames. When gameStarted is false, updateTitle(dt) spins the Rudi preview in the #titleRudi div (rendered by a dedicated titleRenderer and titleScene) and returns early, bypassing all gameplay logic.