Rally
Gemma 4 31b · basic

Reviews & generation details
Reviews
No human review yet. Unreviewed does not mean unsuccessful.
Generation context
Imported historical artifact. Its exact original instructions and lineage are not known.
Generation prompt · exact text unavailable
See the current prompt for reference. It may differ from the instructions used to generate this artifact.
Inspect original source 18,830 bytes · SHA-256 26df50047d51
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>JS Rally Racer 3D</title>
<style>
body { margin: 0; overflow: hidden; background: #87CEEB; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; user-select: none; }
canvas { display: block; }
#ui { position: absolute; top: 20px; left: 20px; color: white; text-shadow: 2px 2px 4px rgba(0,0,0,0.5); pointer-events: none; }
.stat { font-size: 24px; font-weight: bold; margin-bottom: 5px; }
#speedometer { font-size: 48px; color: #ffeb3b; }
#countdown { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 120px; color: white; font-weight: bold; text-shadow: 4px 4px 10px rgba(0,0,0,0.5); pointer-events: none; display: none; }
#menu { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0,0,0,0.8); color: white; padding: 30px; border-radius: 15px; text-align: center; display: none; }
button { padding: 10px 20px; font-size: 20px; cursor: pointer; background: #f44336; color: white; border: none; border-radius: 5px; margin-top: 20px; }
button:hover { background: #d32f2f; }
#minimap { position: absolute; bottom: 20px; right: 20px; width: 200px; height: 200px; background: rgba(0,0,0,0.3); border: 3px solid white; border-radius: 50%; overflow: hidden; }
#minimap-canvas { width: 100%; height: 100%; }
#mobile-controls { position: absolute; bottom: 20px; left: 20px; display: none; }
.btn-ctrl { width: 60px; height: 60px; background: rgba(255,255,255,0.3); border-radius: 50%; display: inline-block; margin: 5px; text-align: center; line-height: 60px; color: white; font-weight: bold; pointer-events: auto; cursor: pointer; }
#touch-right { position: absolute; bottom: 20px; right: 20px; display: none; }
</style>
</head>
<body>
<div id="ui">
<div id="speedometer" class="stat">0 km/h</div>
<div id="lap-info" class="stat">Lap 1/3</div>
<div id="timer" class="stat">00:00.00</div>
<div id="best-lap" class="stat" style="font-size: 18px;">Best: --:--.--</div>
</div>
<div id="countdown">3</div>
<div id="menu">
<h1 id="menu-title">JS RALLY</h1>
<p id="menu-score"></p>
<button onclick="game.reset()">START RACE</button>
</div>
<div id="minimap">
<canvas id="minimap-canvas"></canvas>
</div>
<div id="mobile-controls">
<div class="btn-ctrl" id="btn-left">←</div>
<div class="btn-ctrl" id="btn-right">→</div>
</div>
<div id="touch-right">
<div class="btn-ctrl" id="btn-gas">GAS</div><br>
<div class="btn-ctrl" id="btn-brake">BRK</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.160.0/three.min.js"></script>
<script>
/**
* GAME ENGINE
*/
const game = {
// State
running: false,
paused: false,
countdown: 3,
lap: 1,
totalLaps: 3,
startTime: 0,
currentTime: 0,
bestLapTime: Infinity,
checkpointIndex: 0,
// Physics constants
accel: 0.15,
friction: 0.98,
brakeForce: 0.3,
steerSpeed: 0.04,
offroadMult: 0.4,
// Audio context
audioCtx: null,
engineOsc: null,
engineGain: null,
init() {
this.setupScene();
this.setupInput();
this.createTrack();
this.createCar();
this.createEnvironment();
this.setupUI();
this.loop();
document.getElementById('menu').style.display = 'block';
},
setupScene() {
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0x87CEEB);
this.scene.fog = new THREE.FogExp2(0x87CEEB, 0.005);
this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 2000);
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.shadowMap.enabled = true;
document.body.appendChild(this.renderer.domElement);
const ambient = new THREE.AmbientLight(0xffffff, 0.6);
this.scene.add(ambient);
const sun = new THREE.DirectionalLight(0xffffff, 1);
sun.position.set(100, 200, 100);
sun.castShadow = true;
sun.shadow.camera.left = -200;
sun.shadow.camera.right = 200;
sun.shadow.camera.top = 200;
sun.shadow.camera.bottom = -200;
sun.shadow.mapSize.width = 1024;
sun.shadow.mapSize.height = 1024;
this.scene.add(sun);
this.camMode = 'chase';
},
setupInput() {
this.keys = {};
window.addEventListener('keydown', e => {
this.keys[e.code] = true;
if (e.code === 'KeyC') this.camMode = this.camMode === 'chase' ? 'cinematic' : 'chase';
if (e.code === 'KeyP') this.togglePause();
if (e.code === 'KeyR') this.reset();
});
window.addEventListener('keyup', e => this.keys[e.code] = false);
// Mobile
const bindTouch = (id, code) => {
const el = document.getElementById(id);
el.addEventListener('touchstart', (e) => { e.preventDefault(); this.keys[code] = true; });
el.addEventListener('touchend', (e) => { e.preventDefault(); this.keys[code] = false; });
};
bindTouch('btn-left', 'ArrowLeft');
bindTouch('btn-right', 'ArrowRight');
bindTouch('btn-gas', 'ArrowUp');
bindTouch('btn-brake', 'ArrowDown');
if ('ontouchstart' in window) {
document.getElementById('mobile-controls').style.display = 'block';
document.getElementById('touch-right').style.display = 'block';
}
},
createTrack() {
// Procedural track loop using CatmullRomCurve3
const pts = [
new THREE.Vector3(0, 0, 0),
new THREE.Vector3(100, 0, 50),
new THREE.Vector3(200, 10, 0),
new THREE.Vector3(250, 5, -100),
new THREE.Vector3(150, -5, -200),
new THREE.Vector3(0, 0, -250),
new THREE.Vector3(-150, 10, -200),
new THREE.Vector3(-200, 0, -100),
new THREE.Vector3(-100, -5, -50),
new THREE.Vector3(0, 0, 0),
];
this.trackCurve = new THREE.CatmullRomCurve3(pts);
this.trackPoints = this.trackCurve.getPoints(200);
const trackGeo = new THREE.TubeGeometry(this.trackCurve, 200, 8, 12, true);
const trackMat = new THREE.MeshStandardMaterial({ color: 0x444444, roughness: 0.8 });
this.trackMesh = new THREE.Mesh(trackGeo, trackMat);
this.trackMesh.receiveShadow = true;
this.scene.add(this.trackMesh);
// Grass ground
const groundGeo = new THREE.PlaneGeometry(2000, 2000);
const groundMat = new THREE.MeshStandardMaterial({ color: 0x3a5f0b });
const ground = new THREE.Mesh(groundGeo, groundMat);
ground.rotation.x = -Math.PI / 2;
ground.position.y = -10;
ground.receiveShadow = true;
this.scene.add(ground);
// Checkpoints (invisible)
this.checkpoints = [];
for (let i = 0; i < 10; i++) {
const t = i / 10;
const pos = this.trackCurve.getPointAt(t);
const cp = new THREE.Mesh(
new THREE.BoxGeometry(20, 10, 2),
new THREE.MeshBasicMaterial({ visible: false })
);
cp.position.copy(pos);
cp.lookAt(this.trackCurve.getPointAt((t + 0.01) % 1));
this.scene.add(cp);
this.checkpoints.push(cp);
}
},
createCar() {
const carGroup = new THREE.Group();
// Body
const body = new THREE.Mesh(
new THREE.BoxGeometry(2, 0.8, 4),
new THREE.MeshStandardMaterial({ color: 0xd32f2f })
);
body.position.y = 0.6;
body.castShadow = true;
carGroup.add(body);
// Roof
const roof = new THREE.Mesh(
new THREE.BoxGeometry(1.6, 0.6, 2),
new THREE.MeshStandardMaterial({ color: 0xffffff })
);
roof.position.set(0, 1.3, -0.2);
roof.castShadow = true;
carGroup.add(roof);
// Spoiler
const spoil = new THREE.Mesh(
new THREE.BoxGeometry(2, 0.1, 0.5),
new THREE.MeshStandardMaterial({ color: 0x222222 })
);
spoil.position.set(0, 1.1, 1.8);
carGroup.add(spoil);
// Wheels
const wheelGeo = new THREE.CylinderGeometry(0.4, 0.4, 0.4, 12);
const wheelMat = new THREE.MeshStandardMaterial({ color: 0x111111 });
const wheelPos = [[-1, 0.4, 1.2], [1, 0.4, 1.2], [-1, 0.4, -1.2], [1, 0.4, -1.2]];
wheelPos.forEach(p => {
const w = new THREE.Mesh(wheelGeo, wheelMat);
w.rotation.z = Math.PI / 2;
w.position.set(...p);
carGroup.add(w);
});
this.car = carGroup;
this.car.position.set(0, 0, 0);
this.scene.add(this.car);
// Car physics state
this.carPhys = {
speed: 0,
angle: 0,
drift: 0,
targetRot: 0
};
},
createEnvironment() {
// Simple Trees
for (let i = 0; i < 150; i++) {
const angle = Math.random() * Math.PI * 2;
const dist = 30 + Math.random() * 200;
const x = Math.cos(angle) * dist;
const z = Math.sin(angle) * dist;
const tree = new THREE.Group();
const trunk = new THREE.Mesh(
new THREE.CylinderGeometry(0.5, 0.5, 3),
new THREE.MeshStandardMaterial({ color: 0x5d4037 })
);
trunk.position.y = 1.5;
const leaves = new THREE.Mesh(
new THREE.ConeGeometry(3, 6, 8),
new THREE.MeshStandardMaterial({ color: 0x2e7d32 })
);
leaves.position.y = 5;
tree.add(trunk, leaves);
tree.position.set(x, -10, z);
tree.castShadow = true;
this.scene.add(tree);
}
},
setupUI() {
this.minimapCanvas = document.getElementById('minimap-canvas');
this.mctx = this.minimapCanvas.getContext('2d');
this.minimapCanvas.width = 200;
this.minimapCanvas.height = 200;
},
initAudio() {
if (this.audioCtx) return;
this.audioCtx = new (window.AudioContext || window.webkitAudioContext)();
this.engineOsc = this.audioCtx.createOscillator();
this.engineGain = this.audioCtx.createGain();
this.engineOsc.type = 'sawtooth';
this.engineOsc.frequency.setValueAtTime(100, this.audioCtx.currentTime);
this.engineGain.gain.setValueAtTime(0, this.audioCtx.currentTime);
this.engineOsc.connect(this.engineGain);
this.engineGain.connect(this.audioCtx.destination);
this.engineOsc.start();
},
playSound(freq, type, dur, vol) {
if (!this.audioCtx) return;
const osc = this.audioCtx.createOscillator();
const g = this.audioCtx.createGain();
osc.type = type;
osc.frequency.setValueAtTime(freq, this.audioCtx.currentTime);
g.gain.setValueAtTime(vol, this.audioCtx.currentTime);
g.gain.exponentialRampToValueAtTime(0.01, this.audioCtx.currentTime + dur);
osc.connect(g);
g.connect(this.audioCtx.destination);
osc.start();
osc.stop(this.audioCtx.currentTime + dur);
},
reset() {
this.initAudio();
this.car.position.set(0, 0, 0);
this.carPhys.speed = 0;
this.carPhys.angle = 0;
this.lap = 1;
this.checkpointIndex = 0;
this.bestLapTime = Infinity;
this.running = false;
this.paused = false;
document.getElementById('menu').style.display = 'none';
const cdEl = document.getElementById('countdown');
cdEl.style.display = 'block';
this.countdown = 3;
const timer = setInterval(() => {
this.countdown--;
if (this.countdown > 0) {
cdEl.innerText = this.countdown;
this.playSound(440, 'sine', 0.2, 0.1);
} else if (this.countdown === 0) {
cdEl.innerText = "GO!";
this.playSound(880, 'sine', 0.4, 0.2);
this.startTime = Date.now();
this.running = true;
clearInterval(timer);
setTimeout(() => cdEl.style.display = 'none', 1000);
}
}, 1000);
},
togglePause() {
this.paused = !this.paused;
document.getElementById('menu').style.display = this.paused ? 'block' : 'none';
document.getElementById('menu-title').innerText = "PAUSED";
},
updatePhysics(dt) {
if (!this.running || this.paused) return;
const p = this.carPhys;
// Traction check
let onTrack = false;
const carPos = this.car.position;
const closestPt = this.trackCurve.getPointAt(this.trackCurve.getU(carPos));
if (carPos.distanceTo(closestPt) < 12) onTrack = true;
const currentAccel = this.accel * (onTrack ? 1 : this.offroadMult);
const currentFric = onTrack ? this.friction : 0.95;
// Input
if (this.keys['ArrowUp'] || this.keys['KeyW']) p.speed += currentAccel;
if (this.keys['ArrowDown'] || this.keys['KeyS']) p.speed -= this.brakeForce;
// Steering
const steerDir = (this.keys['ArrowLeft'] || this.keys['KeyA']) ? 1 : (this.keys['ArrowRight'] || this.keys['KeyD']) ? -1 : 0;
const speedFactor = Math.min(1, Math.abs(p.speed) / 10);
p.angle += steerDir * this.steerSpeed * speedFactor * (p.speed > 0 ? 1 : -1);
// Apply speed and friction
p.speed *= currentFric;
// Position update
this.car.position.x += Math.sin(p.angle) * p.speed;
this.car.position.z += Math.cos(p.angle) * p.speed;
this.car.rotation.y = p.angle;
// Simple height following
const height = this.trackCurve.getPointAt(this.trackCurve.getU(this.car.position)).y;
this.car.position.y = THREE.MathUtils.lerp(this.car.position.y, height, 0.1);
// Checkpoints & Laps
const cp = this.checkpoints[this.checkpointIndex];
if (this.car.position.distanceTo(cp.position) < 15) {
this.checkpointIndex = (this.checkpointIndex + 1) % this.checkpoints.length;
if (this.checkpointIndex === 0) {
const lapTime = (Date.now() - this.startTime) / 1000;
if (lapTime < this.bestLapTime) this.bestLapTime = lapTime;
this.lap++;
this.startTime = Date.now();
this.playSound(660, 'square', 0.3, 0.1);
if (this.lap > this.totalLaps) {
this.finishRace();
}
}
}
// Audio update
if (this.engineGain) {
const pitch = 100 + Math.abs(p.speed) * 50;
this.engineOsc.frequency.setTargetAtTime(pitch, this.audioCtx.currentTime, 0.1);
this.engineGain.gain.setTargetAtTime(0.1 + Math.abs(p.speed) * 0.02, this.audioCtx.currentTime, 0.1);
}
},
finishRace() {
this.running = false;
const totalTime = document.getElementById('timer').innerText;
document.getElementById('menu').style.display = 'block';
document.getElementById('menu-title').innerText = "FINISH!";
document.getElementById('menu-score').innerText = `Final Time: ${totalTime}\nBest Lap: ${this.formatTime(this.bestLapTime)}`;
this.playSound(880, 'sine', 0.5, 0.2);
this.playSound(1100, 'sine', 0.8, 0.2);
},
formatTime(s) {
if (s === Infinity) return "--:--.--";
const mins = Math.floor(s / 60);
const secs = Math.floor(s % 60);
const ms = Math.floor((s % 1) * 100);
return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}.${ms.toString().padStart(2, '0')}`;
},
updateCamera() {
const relOffset = new THREE.Vector3(0, 5, -12);
relOffset.applyAxisAngle(new THREE.Vector3(0, 1, 0), this.carPhys.angle);
const targetPos = this.car.position.clone().add(relOffset);
if (this.camMode === 'chase') {
this.camera.position.lerp(targetPos, 0.1);
this.camera.lookAt(this.car.position);
} else {
this.camera.position.lerp(new THREE.Vector3(this.car.position.x, 50, this.car.position.z), 0.05);
this.camera.lookAt(this.car.position);
}
},
updateUI() {
document.getElementById('speedometer').innerText = `${Math.floor(Math.abs(this.carPhys.speed) * 20)} km/h`;
document.getElementById('lap-info').innerText = `Lap ${Math.min(this.lap, this.totalLaps)}/${this.totalLaps}`;
if (this.running) {
const elapsed = (Date.now() - this.startTime) / 1000;
document.getElementById('timer').innerText = this.formatTime(elapsed);
}
document.getElementById('best-lap').innerText = `Best: ${this.formatTime(this.bestLapTime)}`;
// Minimap
const ctx = this.mctx;
ctx.clearRect(0,0,200,200);
ctx.strokeStyle = "white";
ctx.beginPath();
this.trackPoints.forEach((p, i) => {
const mx = 100 + p.x / 3;
const mz = 100 + p.z / 3;
if (i === 0) ctx.moveTo(mx, mz); else ctx.lineTo(mx, mz);
});
ctx.stroke();
ctx.fillStyle = "red";
ctx.beginPath();
ctx.arc(100 + this.car.position.x / 3, 100 + this.car.position.z / 3, 3, 0, Math.PI * 2);
ctx.fill();
},
loop() {
requestAnimationFrame(() => this.loop());
const dt = 1/60;
this.updatePhysics(dt);
this.updateCamera();
this.updateUI();
this.renderer.render(this.scene, this.camera);
}
};
window.addEventListener('resize', () => {
game.camera.aspect = window.innerWidth / window.innerHeight;
game.camera.updateProjectionMatrix();
game.renderer.setSize(window.innerWidth, window.innerHeight);
});
game.init();
</script>
</body>
</html>
<!-- agent-meta {"model":"gemma-4-31b","provider":"cerebras","persona":"basic","sessionId":"8620fae6-7b58-42b6-b0dc-79fda1a72bf7","tokensIn":1139,"tokensOut":6049,"tokensTotal":7188,"turns":1,"toolCalls":0,"failedToolCalls":0,"timestamp":"2026-07-14T21:56:30.205Z"} -->