🏎️ LLM Coding Benchmark Results

Side-Scrolling Car Animation — Ranked by Visual Quality

Generated: 4/24/2026, 10:02:27 PM
⬇️ Sorted: Lowest Quality → Highest Quality ⬇️
Quality order visually evaluated using Claude Sonnet 4.6 vision

Claude Haiku 4.5

🥉 Basic
56.9
TPS
36.9s
Duration
8.2KB
Output
2103
Tokens
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Driving Scene - Canvas Animation</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        body {
            width: 100vw;
            height: 100vh;
            overflow: hidden;
            background: #000;
            font-family: Arial, sans-serif;
        }
        canvas {
            display: block;
            width: 100%;
            height: 100%;
        }
    </style>
</head>
<body>
    <canvas id="canvas"></canvas>
    <script>
        const canvas = document.getElementById('canvas');
        const ctx = canvas.getContext('2d');

        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;

        let time = 0;
        const roadY = canvas.height * 0.6;
        const carX = canvas.width * 0.3;
        const carY = roadY - 40;
        const carWidth = 80;
        const carHeight = 40;
        const wheelRadius = 8;

        class ParallaxLayer {
            constructor(speed, drawFn) {
                this.speed = speed;
                this.drawFn = drawFn;
                this.offset = 0;
            }

            update(distance) {
                this.offset = (this.offset - distance * this.speed) % (canvas.width * 2);
            }

            draw() {
                this.drawFn(this.offset);
                this.drawFn(this.offset + canvas.width * 2);
            }
        }

        function drawSky() {
            const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
            gradient.addColorStop(0, '#87CEEB');
            gradient.addColorStop(0.5, '#FFB366');
            gradient.addColorStop(1, '#FF8844');
            ctx.fillStyle = gradient;
            ctx.fillRect(0, 0, canvas.width, canvas.height);
        }

        function drawDistantMountains(offset) {
            ctx.fillStyle = '#8B7355';
            ctx.globalAlpha = 0.5;
            
            const mountainWidth = canvas.width * 1.5;
            for (let i = -1; i < 3; i++) {
                const x = i * mountainWidth + offset;
                ctx.beginPath();
                ctx.moveTo(x, canvas.height * 0.35);
                ctx.lineTo(x + mountainWidth * 0.3, canvas.height * 0.15);
                ctx.lineTo(x + mountainWidth * 0.6, canvas.height * 0.25);
                ctx.lineTo(x + mountainWidth, canvas.height * 0.35);
                ctx.fill();
            }
            ctx.globalAlpha = 1;
        }

        function drawTrees(offset) {
            ctx.globalAlpha = 0.8;
            const treeSpacing = canvas.width * 0.4;
            
            for (let i = -2; i < 4; i++) {
                const x = i * treeSpacing + offset;
                const treeY = roadY - 60;
                
                ctx.fillStyle = '#654321';
                ctx.fillRect(x + 5, treeY, 10, 50);
                
                ctx.fillStyle = '#228B22';
                ctx.beginPath();
                ctx.arc(x + 10, treeY, 25, 0, Math.PI * 2);
                ctx.fill();
            }
            ctx.globalAlpha = 1;
        }

        function drawPoles(offset) {
            ctx.globalAlpha = 0.7;
            const poleSpacing = canvas.width * 0.25;
            
            for (let i = -2; i < 5; i++) {
                const x = i * poleSpacing + offset;
                ctx.strokeStyle = '#444';
                ctx.lineWidth = 4;
                ctx.beginPath();
                ctx.moveTo(x, roadY - 80);
                ctx.lineTo(x, roadY - 20);
                ctx.stroke();
                
                ctx.fillStyle = '#666';
                ctx.fillRect(x - 15, roadY - 25, 30, 8);
            }
            ctx.globalAlpha = 1;
        }

        function drawRoadside(offset) {
            const roadWidth = canvas.width * 3;
            ctx.fillStyle = '#CCAA66';
            
            for (let i = -1; i < 2; i++) {
                ctx.fillRect(i * roadWidth + offset, roadY + 50, roadWidth, 30);
            }
        }

        function drawGround(offset) {
            const groundWidth = canvas.width * 2.5;
            ctx.fillStyle = '#90EE90';
            
            for (let i = -1; i < 2; i++) {
                ctx.fillRect(i * groundWidth + offset, roadY + 80, groundWidth, canvas.height);
            }
        }

        function drawRoad(offset) {
            const roadWidth = canvas.width * 2;
            ctx.fillStyle = '#222';
            
            for (let i = -1; i < 2; i++) {
                ctx.fillRect(i * roadWidth + offset, roadY, roadWidth, 50);
            }
            
            ctx.strokeStyle = '#FFD700';
            ctx.lineWidth = 3;
            ctx.setLineDash([20, 15]);
            
            for (let i = -1; i < 2; i++) {
                const x = i * roadWidth + offset;
                ctx.beginPath();
                ctx.moveTo(x, roadY + 25);
                ctx.lineTo(x + roadWidth, roadY + 25);
                ctx.stroke();
            }
            ctx.setLineDash([]);
        }

        function drawCar() {
            const wheelY = carY + carHeight - 5;
            const frontWheelX = carX + carWidth - 15;
            const backWheelX = carX + 15;

            ctx.save();

            ctx.fillStyle = '#FF6B6B';
            ctx.fillRect(carX, carY, carWidth, carHeight * 0.7);

            ctx.fillStyle = '#B84040';
            ctx.fillRect(carX + 10, carY - 10, carWidth - 20, carHeight * 0.5);

            ctx.fillStyle = '#87CEEB';
            ctx.fillRect(carX + 15, carY - 5, 15, 12);
            ctx.fillRect(carX + 35, carY - 5, 15, 12);

            ctx.fillStyle = '#333';
            ctx.beginPath();
            ctx.arc(backWheelX, wheelY, wheelRadius, 0, Math.PI * 2);
            ctx.fill();

            ctx.beginPath();
            ctx.arc(frontWheelX, wheelY, wheelRadius, 0, Math.PI * 2);
            ctx.fill();

            const wheelRotation = (time * 0.15) % (Math.PI * 2);

            function drawWheel(x, y, rotation) {
                ctx.save();
                ctx.translate(x, y);
                ctx.rotate(rotation);

                ctx.strokeStyle = '#555';
                ctx.lineWidth = 1;
                for (let i = 0; i < 6; i++) {
                    const angle = (i * Math.PI) / 3;
                    ctx.beginPath();
                    ctx.moveTo(0, 0);
                    ctx.lineTo(Math.cos(angle) * wheelRadius, Math.sin(angle) * wheelRadius);
                    ctx.stroke();
                }

                ctx.restore();
            }

            drawWheel(backWheelX, wheelY, wheelRotation);
            drawWheel(frontWheelX, wheelY, wheelRotation);

            ctx.restore();
        }

        const layers = [
            new ParallaxLayer(0.15, (offset) => {
                ctx.save();
                drawDistantMountains(offset);
                ctx.restore();
            }),
            new ParallaxLayer(0.3, (offset) => {
                ctx.save();
                drawTrees(offset);
                ctx.restore();
            }),
            new ParallaxLayer(0.35, (offset) => {
                ctx.save();
                drawPoles(offset);
                ctx.restore();
            }),
            new ParallaxLayer(0.5, (offset) => {
                ctx.save();
                drawRoadside(offset);
                ctx.restore();
            }),
            new ParallaxLayer(0.6, (offset) => {
                ctx.save();
                drawGround(offset);
                ctx.restore();
            }),
            new ParallaxLayer(0.55, (offset) => {
                ctx.save();
                drawRoad(offset);
                ctx.restore();
            })
        ];

        function animate() {
            drawSky();

            const scrollDistance = 3;
            
            for (const layer of layers) {
                layer.update(scrollDistance);
                layer.draw();
            }

            drawCar();

            time += 1;
            requestAnimationFrame(animate);
        }

        window.addEventListener('resize', () => {
            canvas.width = window.innerWidth;
            canvas.height = window.innerHeight;
        });

        animate();
    </script>
</body>
</html>

Kimi K2.5

🥉 Basic
42.4
TPS
55.0s
Duration
10.4KB
Output
2800
Tokens
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Side-Scrolling Car Animation - Kimi K2.5</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
            background: #000;
        }
        canvas {
            display: block;
        }
    </style>
</head>
<body>
    <canvas id="canvas"></canvas>
    <script>
        const canvas = document.getElementById('canvas');
        const ctx = canvas.getContext('2d');
        
        // Set canvas size
        function resize() {
            canvas.width = window.innerWidth;
            canvas.height = window.innerHeight;
        }
        resize();
        window.addEventListener('resize', resize);
        
        // Animation variables
        let frame = 0;
        const speed = 2;
        
        // Car position
        const car = {
            x: canvas.width * 0.3,
            y: canvas.height * 0.7,
            wheelRotation: 0,
            bounce: 0
        };
        
        // Scrolling layers for parallax
        const layers = [
            { offset: 0, speed: 0.2, elements: [] },  // Far mountains
            { offset: 0, speed: 0.5, elements: [] },  // Hills
            { offset: 0, speed: 1.0, elements: [] },  // Near ground
            { offset: 0, speed: 1.5, elements: [] }   // Roadside elements
        ];
        
        // Generate random elements for each layer
        function generateElements() {
            // Far mountains
            for (let i = 0; i < 5; i++) {
                layers[0].elements.push({
                    x: i * 400,
                    type: 'mountain',
                    height: 150 + Math.random() * 100,
                    width: 300 + Math.random() * 200
                });
            }
            
            // Hills
            for (let i = 0; i < 8; i++) {
                layers[1].elements.push({
                    x: i * 250,
                    type: 'hill',
                    height: 80 + Math.random() * 60,
                    width: 200 + Math.random() * 150
                });
            }
            
            // Ground elements (trees, poles)
            for (let i = 0; i < 15; i++) {
                layers[2].elements.push({
                    x: i * 150,
                    type: Math.random() > 0.5 ? 'tree' : 'pole',
                    height: 60 + Math.random() * 40
                });
            }
            
            // Roadside details
            for (let i = 0; i < 20; i++) {
                layers[3].elements.push({
                    x: i * 100,
                    type: Math.random() > 0.7 ? 'rock' : 'grass',
                    size: 10 + Math.random() * 15
                });
            }
        }
        
        generateElements();
        
        // Draw sky with gradient (sunset)
        function drawSky() {
            const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
            gradient.addColorStop(0, '#2c3e50');
            gradient.addColorStop(0.3, '#4a235a');
            gradient.addColorStop(0.6, '#d35400');
            gradient.addColorStop(1, '#f39c12');
            ctx.fillStyle = gradient;
            ctx.fillRect(0, 0, canvas.width, canvas.height);
            
            // Sun
            const sunGradient = ctx.createRadialGradient(
                canvas.width * 0.7, canvas.height * 0.3, 0,
                canvas.width * 0.7, canvas.height * 0.3, 100
            );
            sunGradient.addColorStop(0, 'rgba(255, 200, 100, 0.8)');
            sunGradient.addColorStop(0.5, 'rgba(255, 150, 50, 0.4)');
            sunGradient.addColorStop(1, 'rgba(255, 100, 0, 0)');
            ctx.fillStyle = sunGradient;
            ctx.beginPath();
            ctx.arc(canvas.width * 0.7, canvas.height * 0.3, 100, 0, Math.PI * 2);
            ctx.fill();
        }
        
        // Draw mountains
        function drawMountains(layer) {
            ctx.fillStyle = '#1a252f';
            layer.elements.forEach(el => {
                const x = (el.x - layer.offset) % (canvas.width + 400);
                const drawX = x < -200 ? x + canvas.width + 400 : x;
                
                ctx.beginPath();
                ctx.moveTo(drawX, canvas.height * 0.6);
                ctx.lineTo(drawX + el.width / 2, canvas.height * 0.6 - el.height);
                ctx.lineTo(drawX + el.width, canvas.height * 0.6);
                ctx.closePath();
                ctx.fill();
            });
        }
        
        // Draw hills
        function drawHills(layer) {
            ctx.fillStyle = '#2d5016';
            layer.elements.forEach(el => {
                const x = (el.x - layer.offset) % (canvas.width + 300);
                const drawX = x < -150 ? x + canvas.width + 300 : x;
                
                ctx.beginPath();
                ctx.ellipse(
                    drawX + el.width / 2,
                    canvas.height * 0.65,
                    el.width / 2,
                    el.height,
                    0, 0, Math.PI * 2
                );
                ctx.fill();
            });
        }
        
        // Draw ground elements
        function drawGroundElements(layer) {
            layer.elements.forEach(el => {
                const x = (el.x - layer.offset) % (canvas.width + 200);
                const drawX = x < -100 ? x + canvas.width + 200 : x;
                
                if (el.type === 'tree') {
                    // Tree trunk
                    ctx.fillStyle = '#5d4037';
                    ctx.fillRect(drawX - 3, canvas.height * 0.65 - el.height, 6, el.height);
                    // Tree top
                    ctx.fillStyle = '#1b5e20';
                    ctx.beginPath();
                    ctx.moveTo(drawX - 25, canvas.height * 0.65 - el.height);
                    ctx.lineTo(drawX, canvas.height * 0.65 - el.height - 50);
                    ctx.lineTo(drawX + 25, canvas.height * 0.65 - el.height);
                    ctx.closePath();
                    ctx.fill();
                } else {
                    // Pole
                    ctx.fillStyle = '#555';
                    ctx.fillRect(drawX - 2, canvas.height * 0.65 - el.height, 4, el.height);
                }
            });
        }
        
        // Draw car
        function drawCar() {
            const x = car.x;
            const y = car.y + Math.sin(car.bounce) * 3;
            
            // Car body shadow
            ctx.fillStyle = 'rgba(0,0,0,0.3)';
            ctx.fillRect(x - 50, canvas.height * 0.72, 100, 10);
            
            // Car body
            ctx.fillStyle = '#c0392b';
            ctx.beginPath();
            ctx.moveTo(x - 50, y);
            ctx.lineTo(x - 40, y - 30);
            ctx.lineTo(x + 20, y - 30);
            ctx.lineTo(x + 50, y);
            ctx.closePath();
            ctx.fill();
            
            // Car cabin
            ctx.fillStyle = '#922b21';
            ctx.beginPath();
            ctx.moveTo(x - 30, y - 30);
            ctx.lineTo(x - 20, y - 50);
            ctx.lineTo(x + 10, y - 50);
            ctx.lineTo(x + 15, y - 30);
            ctx.closePath();
            ctx.fill();
            
            // Windows
            ctx.fillStyle = '#aed6f1';
            ctx.fillRect(x - 18, y - 48, 20, 15);
            
            // Wheels
            const wheelY = y + 10;
            [-25, 25].forEach(offset => {
                const wx = x + offset;
                
                // Tire
                ctx.fillStyle = '#2c3e50';
                ctx.beginPath();
                ctx.arc(wx, wheelY, 15, 0, Math.PI * 2);
                ctx.fill();
                
                // Rim
                ctx.fillStyle = '#bdc3c7';
                ctx.beginPath();
                ctx.arc(wx, wheelY, 8, 0, Math.PI * 2);
                ctx.fill();
                
                // Spokes (rotating)
                ctx.strokeStyle = '#7f8c8d';
                ctx.lineWidth = 2;
                for (let i = 0; i < 4; i++) {
                    const angle = car.wheelRotation + (i * Math.PI / 2);
                    ctx.beginPath();
                    ctx.moveTo(wx, wheelY);
                    ctx.lineTo(wx + Math.cos(angle) * 8, wheelY + Math.sin(angle) * 8);
                    ctx.stroke();
                }
            });
            
            // Headlights
            ctx.fillStyle = '#f1c40f';
            ctx.beginPath();
            ctx.arc(x + 48, y - 10, 5, 0, Math.PI * 2);
            ctx.fill();
            
            // Light beam
            const beamGradient = ctx.createLinearGradient(x + 50, y - 10, x + 200, y + 20);
            beamGradient.addColorStop(0, 'rgba(241, 196, 15, 0.3)');
            beamGradient.addColorStop(1, 'rgba(241, 196, 15, 0)');
            ctx.fillStyle = beamGradient;
            ctx.beginPath();
            ctx.moveTo(x + 50, y - 10);
            ctx.lineTo(x + 200, y - 30);
            ctx.lineTo(x + 200, y + 30);
            ctx.closePath();
            ctx.fill();
        }
        
        // Draw road
        function drawRoad() {
            // Road surface
            ctx.fillStyle = '#34495e';
            ctx.fillRect(0, canvas.height * 0.72, canvas.width, canvas.height * 0.28);
            
            // Road lines
            ctx.strokeStyle = '#ecf0f1';
            ctx.lineWidth = 3;
            ctx.setLineDash([40, 40]);
            const lineOffset = (frame * speed) % 80;
            ctx.beginPath();
            ctx.moveTo(-80 + lineOffset, canvas.height * 0.85);
            ctx.lineTo(canvas.width + 80, canvas.height * 0.85);
            ctx.stroke();
            ctx.setLineDash([]);
        }
        
        // Animation loop
        function animate() {
            frame++;
            
            // Update car
            car.wheelRotation += 0.15;
            car.bounce += 0.1;
            
            // Update layer offsets
            layers.forEach(layer => {
                layer.offset += layer.speed * speed;
            });
            
            // Clear canvas
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            
            // Draw scene back to front
            drawSky();
            drawMountains(layers[0]);
            drawHills(layers[1]);
            drawGroundElements(layers[2]);
            drawRoad();
            drawCar();
            
            requestAnimationFrame(animate);
        }
        
        animate();
    </script>
</body>
</html>

Qwen 3.6 27B

🥈 Better
58.1
TPS
1.2m
Duration
12.0KB
Output
0
Tokens
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Side-Scrolling Car Animation - Qwen 3.6 27B</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body { overflow: hidden; background: #000; }
        canvas { display: block; }
    </style>
</head>
<body>
    <canvas id="gameCanvas"></canvas>
    <script>
        const canvas = document.getElementById('gameCanvas');
        const ctx = canvas.getContext('2d');
        
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
        
        window.addEventListener('resize', () => {
            canvas.width = window.innerWidth;
            canvas.height = window.innerHeight;
        });
        
        // Scene elements
        let scrollX = 0;
        const car = {
            x: window.innerWidth * 0.2,
            y: window.innerHeight * 0.68,
            wheelAngle: 0,
            bodyOffset: 0
        };
        
        // Background layers with parallax speeds
        const bgLayers = [
            { speed: 0.1, items: [], type: 'mountain' },
            { speed: 0.3, items: [], type: 'hill' },
            { speed: 0.8, items: [], type: 'tree' },
            { speed: 1.2, items: [], type: 'ground' }
        ];
        
        // Generate background items
        function initBackground() {
            // Mountains (far)
            for (let i = 0; i < 6; i++) {
                bgLayers[0].items.push({
                    x: i * 500,
                    w: 400 + Math.random() * 200,
                    h: 200 + Math.random() * 150
                });
            }
            
            // Hills (mid-far)
            for (let i = 0; i < 10; i++) {
                bgLayers[1].items.push({
                    x: i * 300,
                    w: 250 + Math.random() * 100,
                    h: 100 + Math.random() * 80
                });
            }
            
            // Trees (mid-near)
            for (let i = 0; i < 12; i++) {
                bgLayers[2].items.push({
                    x: i * 200,
                    h: 80 + Math.random() * 60,
                    variant: Math.floor(Math.random() * 3)
                });
            }
            
            // Ground details (near)
            for (let i = 0; i < 20; i++) {
                bgLayers[3].items.push({
                    x: i * 120,
                    type: Math.random() > 0.6 ? 'rock' : 'bush',
                    size: 15 + Math.random() * 20
                });
            }
        }
        
        initBackground();
        
        // Draw gradient sky (dusk theme)
        function drawSky() {
            const g = ctx.createLinearGradient(0, 0, 0, canvas.height);
            g.addColorStop(0, '#1a1a2e');
            g.addColorStop(0.4, '#16213e');
            g.addColorStop(0.7, '#e94560');
            g.addColorStop(1, '#f39c12');
            ctx.fillStyle = g;
            ctx.fillRect(0, 0, canvas.width, canvas.height);
            
            // Distant stars
            ctx.fillStyle = 'rgba(255,255,255,0.5)';
            for (let i = 0; i < 50; i++) {
                const sx = (i * 137) % canvas.width;
                const sy = (i * 73) % (canvas.height * 0.4);
                ctx.fillRect(sx, sy, 2, 2);
            }
            
            // Sun setting
            const sunG = ctx.createRadialGradient(
                canvas.width * 0.8, canvas.height * 0.4, 0,
                canvas.width * 0.8, canvas.height * 0.4, 120
            );
            sunG.addColorStop(0, 'rgba(255, 100, 50, 0.6)');
            sunG.addColorStop(0.5, 'rgba(255, 50, 50, 0.2)');
            sunG.addColorStop(1, 'rgba(255, 0, 0, 0)');
            ctx.fillStyle = sunG;
            ctx.beginPath();
            ctx.arc(canvas.width * 0.8, canvas.height * 0.4, 120, 0, Math.PI * 2);
            ctx.fill();
        }
        
        // Draw mountains (layer 0)
        function drawMountains(layer) {
            ctx.fillStyle = '#0f0f23';
            layer.items.forEach(item => {
                const x = (item.x - scrollX * layer.speed) % (canvas.width + 600);
                const drawX = x < -300 ? x + canvas.width + 600 : x - 300;
                
                ctx.beginPath();
                ctx.moveTo(drawX, canvas.height * 0.55);
                ctx.lineTo(drawX + item.w / 2, canvas.height * 0.55 - item.h);
                ctx.lineTo(drawX + item.w, canvas.height * 0.55);
                ctx.lineTo(drawX, canvas.height * 0.55);
                ctx.fill();
            });
        }
        
        // Draw hills (layer 1)
        function drawHills(layer) {
            ctx.fillStyle = '#1e3a1e';
            layer.items.forEach(item => {
                const x = (item.x - scrollX * layer.speed) % (canvas.width + 400);
                const drawX = x < -200 ? x + canvas.width + 400 : x - 200;
                
                ctx.beginPath();
                ctx.ellipse(drawX + item.w/2, canvas.height * 0.6, item.w/2, item.h, 0, 0, Math.PI * 2);
                ctx.fill();
            });
        }
        
        // Draw trees (layer 2)
        function drawTrees(layer) {
            layer.items.forEach(item => {
                const x = (item.x - scrollX * layer.speed) % (canvas.width + 300);
                const drawX = x < -100 ? x + canvas.width + 300 : x - 100;
                const y = canvas.height * 0.62;
                
                // Trunk
                ctx.fillStyle = '#3d2817';
                ctx.fillRect(drawX - 4, y - item.h, 8, item.h);
                
                // Tree crown based on variant
                const colors = ['#2d5016', '#1e3a1e', '#3d6b1e'];
                ctx.fillStyle = colors[item.variant];
                
                ctx.beginPath();
                ctx.arc(drawX, y - item.h, 30, 0, Math.PI * 2);
                ctx.arc(drawX - 15, y - item.h + 10, 25, 0, Math.PI * 2);
                ctx.arc(drawX + 15, y - item.h + 10, 25, 0, Math.PI * 2);
                ctx.fill();
            });
        }
        
        // Draw ground details (layer 3)
        function drawGroundDetails(layer) {
            layer.items.forEach(item => {
                const x = (item.x - scrollX * layer.speed) % (canvas.width + 250);
                const drawX = x < -50 ? x + canvas.width + 250 : x - 50;
                const y = canvas.height * 0.72;
                
                if (item.type === 'rock') {
                    ctx.fillStyle = '#5a5a5a';
                    ctx.beginPath();
                    ctx.ellipse(drawX, y + 20, item.size, item.size * 0.6, 0, 0, Math.PI * 2);
                    ctx.fill();
                } else {
                    ctx.fillStyle = '#3d5c1e';
                    ctx.beginPath();
                    ctx.ellipse(drawX, y + 15, item.size * 1.5, item.size * 0.4, 0, 0, Math.PI * 2);
                    ctx.fill();
                }
            });
        }
        
        // Draw road
        function drawRoad() {
            ctx.fillStyle = '#2c3e50';
            ctx.fillRect(0, canvas.height * 0.72, canvas.width, canvas.height * 0.28);
            
            // Road edge
            ctx.fillStyle = '#34495e';
            ctx.fillRect(0, canvas.height * 0.72, canvas.width, 5);
            
            // Moving road lines
            ctx.strokeStyle = '#ecf0f1';
            ctx.lineWidth = 4;
            ctx.setLineDash([50, 50]);
            const dashOffset = -(scrollX * 1.5) % 100;
            ctx.lineDashOffset = dashOffset;
            ctx.beginPath();
            ctx.moveTo(0, canvas.height * 0.86);
            ctx.lineTo(canvas.width, canvas.height * 0.86);
            ctx.stroke();
            ctx.setLineDash([]);
            ctx.lineDashOffset = 0;
        }
        
        // Draw car
        function drawCar() {
            const cx = car.x;
            const cy = car.y + Math.sin(car.bodyOffset) * 2;
            
            // Shadow
            ctx.fillStyle = 'rgba(0,0,0,0.25)';
            ctx.beginPath();
            ctx.ellipse(cx, canvas.height * 0.76, 60, 10, 0, 0, Math.PI * 2);
            ctx.fill();
            
            // Car body (red sports car)
            ctx.fillStyle = '#c0392b';
            
            // Lower body
            ctx.beginPath();
            ctx.moveTo(cx - 55, cy);
            ctx.lineTo(cx - 45, cy - 25);
            ctx.lineTo(cx + 35, cy - 25);
            ctx.lineTo(cx + 55, cy);
            ctx.closePath();
            ctx.fill();
            
            // Upper cabin
            ctx.fillStyle = '#a93226';
            ctx.beginPath();
            ctx.moveTo(cx - 35, cy - 25);
            ctx.lineTo(cx - 25, cy - 50);
            ctx.lineTo(cx + 20, cy - 50);
            ctx.lineTo(cx + 30, cy - 25);
            ctx.closePath();
            ctx.fill();
            
            // Windshield
            ctx.fillStyle = '#85c1e9';
            ctx.beginPath();
            ctx.moveTo(cx - 20, cy - 48);
            ctx.lineTo(cx + 15, cy - 48);
            ctx.lineTo(cx + 25, cy - 25);
            ctx.lineTo(cx - 18, cy - 25);
            ctx.closePath();
            ctx.fill();
            
            // Side window
            ctx.fillStyle = '#5dade2';
            ctx.fillRect(cx - 30, cy - 45, 25, 18);
            
            // Wheels
            const wheelY = cy + 8;
            [-30, 30].forEach((offset, i) => {
                const wx = cx + offset;
                
                // Tire
                ctx.fillStyle = '#1c2833';
                ctx.beginPath();
                ctx.arc(wx, wheelY, 16, 0, Math.PI * 2);
                ctx.fill();
                
                // Rim
                ctx.fillStyle = '#95a5a6';
                ctx.beginPath();
                ctx.arc(wx, wheelY, 10, 0, Math.PI * 2);
                ctx.fill();
                
                // Rotating spokes
                ctx.strokeStyle = '#555';
                ctx.lineWidth = 3;
                for (let j = 0; j < 5; j++) {
                    const angle = car.wheelAngle + (j * Math.PI * 2 / 5);
                    ctx.beginPath();
                    ctx.moveTo(wx, wheelY);
                    ctx.lineTo(wx + Math.cos(angle) * 10, wheelY + Math.sin(angle) * 10);
                    ctx.stroke();
                }
                
                // Wheel hub
                ctx.fillStyle = '#333';
                ctx.beginPath();
                ctx.arc(wx, wheelY, 4, 0, Math.PI * 2);
                ctx.fill();
            });
            
            // Headlights
            ctx.fillStyle = '#f1c40f';
            ctx.beginPath();
            ctx.ellipse(cx + 52, cy - 12, 6, 4, 0, 0, Math.PI * 2);
            ctx.fill();
            
            // Taillights
            ctx.fillStyle = '#e74c3c';
            ctx.beginPath();
            ctx.ellipse(cx - 52, cy - 8, 4, 3, 0, 0, Math.PI * 2);
            ctx.fill();
            
            // Headlight beam
            const beamGrad = ctx.createLinearGradient(cx + 55, cy - 12, cx + 250, cy + 20);
            beamGrad.addColorStop(0, 'rgba(241, 196, 15, 0.4)');
            beamGrad.addColorStop(0.5, 'rgba(241, 196, 15, 0.1)');
            beamGrad.addColorStop(1, 'rgba(241, 196, 15, 0)');
            ctx.fillStyle = beamGrad;
            ctx.beginPath();
            ctx.moveTo(cx + 55, cy - 12);
            ctx.lineTo(cx + 250, cy - 40);
            ctx.lineTo(cx + 250, cy + 30);
            ctx.closePath();
            ctx.fill();
        }
        
        // Main loop
        function loop() {
            // Update
            scrollX += 3;
            car.wheelAngle += 0.2;
            car.bodyOffset += 0.15;
            
            // Clear
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            
            // Draw back to front
            drawSky();
            drawMountains(bgLayers[0]);
            drawHills(bgLayers[1]);
            drawTrees(bgLayers[2]);
            drawRoad();
            drawGroundDetails(bgLayers[3]);
            drawCar();
            
            requestAnimationFrame(loop);
        }
        
        loop();
    </script>
</body>
</html>

Gemini 3.1 Pro

🥈 Better
34.1
TPS
55.0s
Duration
7.3KB
Output
1874
Tokens
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cinematic Drive</title>
<style>
    body, html { margin: 0; padding: 0; overflow: hidden; background: #000; width: 100%; height: 100%; }
    canvas { display: block; width: 100vw; height: 100vh; }
</style>
</head>
<body>
<canvas id="scene"></canvas>
<script>
    const canvas = document.getElementById('scene');
    const ctx = canvas.getContext('2d');
    let width, height;

    function resize() {
        width = canvas.width = window.innerWidth;
        height = canvas.height = window.innerHeight;
    }
    window.addEventListener('resize', resize);
    resize();

    let t = 0;

    function drawMountains(yBase, amplitude, frequency, offset, color) {
        ctx.fillStyle = color;
        ctx.beginPath();
        ctx.moveTo(0, height);
        for (let x = 0; x <= width; x += 10) {
            let y = yBase + Math.sin((x + offset) * frequency) * amplitude
                          + Math.sin((x + offset) * frequency * 0.5) * amplitude * 0.5;
            ctx.lineTo(x, y);
        }
        ctx.lineTo(width, height);
        ctx.closePath();
        ctx.fill();
    }

    function loop() {
        t += 0.016; // Approx 60fps time step
        ctx.clearRect(0, 0, width, height);

        // 1. Sky Gradient (Sunset/Dusk)
        let sky = ctx.createLinearGradient(0, 0, 0, height * 0.6);
        sky.addColorStop(0, "#1a0b2e");
        sky.addColorStop(0.4, "#4b1d52");
        sky.addColorStop(0.8, "#d15c55");
        sky.addColorStop(1, "#f2a65a");
        ctx.fillStyle = sky;
        ctx.fillRect(0, 0, width, height);

        // 2. Setting Sun
        ctx.fillStyle = "#ffcc77";
        ctx.shadowColor = "#ffaa00";
        ctx.shadowBlur = 60;
        ctx.beginPath();
        ctx.arc(width * 0.7, height * 0.45, 60, 0, Math.PI * 2);
        ctx.fill();
        ctx.shadowBlur = 0;

        // 3. Parallax Mountains (Distant, Mid, Near)
        drawMountains(height * 0.5, 40, 0.003, t * 20, "#311442");
        drawMountains(height * 0.55, 60, 0.005, t * 50, "#230e30");
        drawMountains(height * 0.65, 30, 0.01, t * 120, "#110518");

        // 4. Ground base
        let groundY = height * 0.75;
        ctx.fillStyle = "#0a030f";
        ctx.fillRect(0, groundY, width, height - groundY);

        // 5. Scenery: Passing Trees/Poles (Layer behind road)
        ctx.fillStyle = "#050108";
        let treeSpeed = t * 600;
        let treeSpace = 350;
        for (let x = -(treeSpeed % treeSpace); x < width + treeSpace; x += treeSpace) {
            let hOff = Math.abs(Math.sin((x + treeSpeed) * 0.01)) * 80;
            ctx.fillRect(x + 50, groundY - 60 - hOff, 15, 80 + hOff);
            ctx.beginPath();
            ctx.arc(x + 57, groundY - 60 - hOff, 35, 0, Math.PI*2);
            ctx.fill();
        }

        // 6. Road Surface & Markings
        ctx.fillStyle = "#1e1e24";
        ctx.fillRect(0, groundY + 30, width, 80);
        ctx.fillStyle = "#dddddd";
        let dashSpeed = t * 1200;
        let dashLen = 120;
        let gap = 120;
        let totalDash = dashLen + gap;
        for (let x = -(dashSpeed % totalDash); x < width; x += totalDash) {
            ctx.fillRect(x, groundY + 65, dashLen, 8);
        }

        // 7. Foreground Poles (Fastest)
        ctx.fillStyle = "#020103";
        let poleSpeed = t * 2000;
        let poleSpace = 800;
        for (let x = -(poleSpeed % poleSpace); x < width + poleSpace; x += poleSpace) {
            ctx.fillRect(x, groundY + 110, 30, height);
        }

        // 8. The Car
        let cx = width * 0.3;
        let cy = groundY - 20;
        
        // Subtle suspension bounce
        let bounce = Math.sin(t * 18) * 1.5 + Math.sin(t * 31) * 0.5;
        cy += bounce;

        // Shadow beneath car
        ctx.fillStyle = "rgba(0,0,0,0.6)";
        ctx.beginPath();
        ctx.ellipse(cx, groundY + 40, 180, 12, 0, 0, Math.PI * 2);
        ctx.fill();

        ctx.save();
        ctx.translate(cx, cy);

        // Car Body (Cinematic silhouette profile)
        ctx.fillStyle = "#0b0c10";
        ctx.beginPath();
        ctx.moveTo(-160, -10);
        ctx.lineTo(-150, -35); // trunk
        ctx.lineTo(-90, -42); // rear window
        ctx.lineTo(-20, -70); // roof
        ctx.lineTo(40, -70); // roof front
        ctx.lineTo(95, -35); // windshield
        ctx.lineTo(165, -20); // hood
        ctx.lineTo(175, 10); // front bumper
        ctx.lineTo(165, 30); // bottom front
        ctx.lineTo(-160, 30); // bottom rear
        ctx.lineTo(-170, 10); // rear bumper
        ctx.fill();

        // Windows (dark reflections)
        ctx.fillStyle = "#22252c";
        ctx.beginPath();
        ctx.moveTo(-80, -40);
        ctx.lineTo(-22, -65);
        ctx.lineTo(5, -65);
        ctx.lineTo(5, -36);
        ctx.fill();

        ctx.beginPath();
        ctx.moveTo(12, -65);
        ctx.lineTo(38, -65);
        ctx.lineTo(85, -35);
        ctx.lineTo(12, -35);
        ctx.fill();

        // Headlight Beam
        let flicker = Math.random() * 0.05 + 0.95;
        ctx.shadowColor = "#e0f7fa";
        ctx.shadowBlur = 25;
        ctx.fillStyle = "#e0f7fa";
        ctx.beginPath();
        ctx.arc(170, -5, 4, 0, Math.PI*2);
        ctx.fill();
        
        ctx.shadowBlur = 0;
        ctx.globalAlpha = 0.2 * flicker;
        ctx.beginPath();
        ctx.moveTo(170, -5);
        ctx.lineTo(800, -25);
        ctx.lineTo(800, 80);
        ctx.fill();
        ctx.globalAlpha = 1.0;

        // Taillight
        ctx.shadowColor = "#ff1744";
        ctx.shadowBlur = 20;
        ctx.fillStyle = "#ff1744";
        ctx.beginPath();
        ctx.arc(-165, -12, 5, 0, Math.PI*2);
        ctx.fill();
        ctx.shadowBlur = 0;

        // Wheels Function
        function drawWheel(wx, wy) {
            ctx.save();
            ctx.translate(wx, wy);
            ctx.translate(0, -bounce * 0.8); // Wheels stay relatively grounded
            ctx.rotate(t * 18); // Wheel spin
            
            // Tire
            ctx.fillStyle = "#111";
            ctx.beginPath();
            ctx.arc(0, 0, 34, 0, Math.PI * 2);
            ctx.fill();
            
            // Rim
            ctx.strokeStyle = "#444";
            ctx.lineWidth = 4;
            ctx.beginPath();
            ctx.arc(0, 0, 22, 0, Math.PI * 2);
            ctx.stroke();

            // Spokes
            ctx.strokeStyle = "#888";
            ctx.lineWidth = 3;
            for(let i=0; i<6; i++) {
                ctx.beginPath();
                ctx.moveTo(0,0);
                ctx.lineTo(22, 0);
                ctx.stroke();
                ctx.rotate(Math.PI / 3);
            }
            ctx.restore();
        }

        // Front and Rear Wheels
        drawWheel(-100, 30);
        drawWheel(110, 30);

        ctx.restore();

        // 9. Speed Lines (Foreground motion blur)
        ctx.fillStyle = "rgba(255,255,255,0.06)";
        let fgSpeed = t * 2800;
        for(let i=0; i<4; i++) {
            let y = groundY + 90 + i * 15;
            let dashL = 250 + i * 80;
            let spc = 500;
            let x = -((fgSpeed * (1 + i*0.1)) % spc) + (i*100);
            ctx.fillRect(x, y, dashL, 3);
            ctx.fillRect(x + spc, y, dashL, 3);
            ctx.fillRect(x + spc*2, y, dashL, 3);
        }

        requestAnimationFrame(loop);
    }

    // Start loop
    requestAnimationFrame(loop);
</script>
</body>
</html>

GPT-5.5

🥇 Best
63.8
TPS
1.0m
Duration
15.0KB
Output
3834
Tokens
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Car Journey</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        body {
            overflow: hidden;
            background: #000;
        }
        canvas {
            display: block;
            width: 100vw;
            height: 100vh;
        }
    </style>
</head>
<body>
    <canvas id="canvas"></canvas>
    <script>
        const canvas = document.getElementById('canvas');
        const ctx = canvas.getContext('2d');
        
        function resize() {
            canvas.width = window.innerWidth;
            canvas.height = window.innerHeight;
        }
        resize();
        window.addEventListener('resize', resize);
        
        // Animation state
        let time = 0;
        const speed = 2;
        
        // Parallax layers with different speeds
        const layers = {
            sky: { speed: 0.1, offset: 0 },
            mountains: { speed: 0.2, offset: 0 },
            hills: { speed: 0.4, offset: 0 },
            trees: { speed: 0.8, offset: 0 },
            poles: { speed: 1.2, offset: 0 },
            ground: { speed: 2, offset: 0 }
        };
        
        // Car physics
        const car = {
            x: 0,
            y: 0,
            wheelRotation: 0,
            bounce: 0,
            bouncePhase: 0
        };
        
        // Scenery elements
        const trees = [];
        const poles = [];
        const clouds = [];
        
        // Initialize scenery
        function initScenery() {
            for (let i = 0; i < 8; i++) {
                trees.push({
                    x: Math.random() * canvas.width * 2,
                    y: 0,
                    type: Math.floor(Math.random() * 3),
                    scale: 0.8 + Math.random() * 0.4
                });
            }
            for (let i = 0; i < 6; i++) {
                poles.push({
                    x: Math.random() * canvas.width * 2,
                    height: 80 + Math.random() * 40
                });
            }
            for (let i = 0; i < 5; i++) {
                clouds.push({
                    x: Math.random() * canvas.width,
                    y: 50 + Math.random() * 150,
                    scale: 0.5 + Math.random() * 0.8,
                    speed: 0.1 + Math.random() * 0.2
                });
            }
        }
        
        // Draw gradient sky with sunset colors
        function drawSky() {
            const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height * 0.7);
            gradient.addColorStop(0, '#1a1a3e');
            gradient.addColorStop(0.3, '#4a3b6e');
            gradient.addColorStop(0.5, '#d4706e');
            gradient.addColorStop(0.7, '#f4a261');
            gradient.addColorStop(1, '#e9c46a');
            
            ctx.fillStyle = gradient;
            ctx.fillRect(0, 0, canvas.width, canvas.height);
            
            // Draw sun
            const sunY = canvas.height * 0.35;
            const sunGradient = ctx.createRadialGradient(
                canvas.width * 0.7, sunY, 0,
                canvas.width * 0.7, sunY, 80
            );
            sunGradient.addColorStop(0, 'rgba(255, 200, 100, 0.9)');
            sunGradient.addColorStop(0.5, 'rgba(255, 150, 80, 0.5)');
            sunGradient.addColorStop(1, 'rgba(255, 100, 60, 0)');
            
            ctx.fillStyle = sunGradient;
            ctx.beginPath();
            ctx.arc(canvas.width * 0.7, sunY, 80, 0, Math.PI * 2);
            ctx.fill();
            
            // Draw clouds
            ctx.fillStyle = 'rgba(255, 200, 180, 0.3)';
            clouds.forEach(cloud => {
                cloud.x -= cloud.speed;
                if (cloud.x < -200) cloud.x = canvas.width + 200;
                
                ctx.beginPath();
                ctx.arc(cloud.x, cloud.y, 30 * cloud.scale, 0, Math.PI * 2);
                ctx.arc(cloud.x + 25 * cloud.scale, cloud.y - 10 * cloud.scale, 35 * cloud.scale, 0, Math.PI * 2);
                ctx.arc(cloud.x + 50 * cloud.scale, cloud.y, 30 * cloud.scale, 0, Math.PI * 2);
                ctx.fill();
            });
        }
        
        // Draw distant mountains
        function drawMountains() {
            layers.mountains.offset -= layers.mountains.speed;
            if (layers.mountains.offset < -canvas.width) layers.mountains.offset = 0;
            
            const mountainY = canvas.height * 0.55;
            
            ctx.fillStyle = '#2d3561';
            for (let offset of [layers.mountains.offset, layers.mountains.offset + canvas.width]) {
                ctx.beginPath();
                ctx.moveTo(offset, canvas.height);
                
                // Generate mountain peaks
                const peaks = [0.1, 0.25, 0.4, 0.6, 0.75, 0.9];
                peaks.forEach((peak, i) => {
                    const x = offset + peak * canvas.width;
                    const height = 100 + Math.sin(i * 2.5) * 60;
                    ctx.lineTo(x, mountainY - height);
                });
                
                ctx.lineTo(offset + canvas.width, canvas.height);
                ctx.fill();
            }
        }
        
        // Draw rolling hills
        function drawHills() {
            layers.hills.offset -= layers.hills.speed;
            if (layers.hills.offset < -canvas.width) layers.hills.offset = 0;
            
            const hillY = canvas.height * 0.65;
            
            ctx.fillStyle = '#3d5a40';
            for (let offset of [layers.hills.offset, layers.hills.offset + canvas.width]) {
                ctx.beginPath();
                ctx.moveTo(offset, canvas.height);
                
                for (let x = 0; x <= canvas.width; x += 20) {
                    const y = hillY - Math.sin((x + offset) * 0.01) * 40 - Math.sin((x + offset) * 0.003) * 20;
                    ctx.lineTo(offset + x, y);
                }
                
                ctx.lineTo(offset + canvas.width, canvas.height);
                ctx.fill();
            }
        }
        
        // Draw trees
        function drawTrees() {
            layers.trees.offset -= layers.trees.speed;
            if (layers.trees.offset < -canvas.width * 2) layers.trees.offset = 0;
            
            const groundY = canvas.height * 0.78;
            
            trees.forEach(tree => {
                let x = tree.x + layers.trees.offset;
                while (x < -100) x += canvas.width * 2;
                
                if (x > canvas.width + 100) return;
                
                const y = groundY;
                const scale = tree.scale;
                
                // Tree trunk
                ctx.fillStyle = '#4a3728';
                ctx.fillRect(x - 4 * scale, y - 60 * scale, 8 * scale, 60 * scale);
                
                // Tree foliage
                ctx.fillStyle = '#2d5016';
                ctx.beginPath();
                ctx.moveTo(x - 25 * scale, y - 50 * scale);
                ctx.lineTo(x, y - 120 * scale);
                ctx.lineTo(x + 25 * scale, y - 50 * scale);
                ctx.fill();
                
                ctx.beginPath();
                ctx.moveTo(x - 20 * scale, y - 80 * scale);
                ctx.lineTo(x, y - 140 * scale);
                ctx.lineTo(x + 20 * scale, y - 80 * scale);
                ctx.fill();
            });
        }
        
        // Draw utility poles
        function drawPoles() {
            layers.poles.offset -= layers.poles.speed;
            if (layers.poles.offset < -canvas.width * 2) layers.poles.offset = 0;
            
            const groundY = canvas.height * 0.78;
            
            poles.forEach(pole => {
                let x = pole.x + layers.poles.offset;
                while (x < -50) x += canvas.width * 2;
                
                if (x > canvas.width + 50) return;
                
                // Pole
                ctx.fillStyle = '#5a5a5a';
                ctx.fillRect(x - 2, groundY - pole.height, 4, pole.height);
                
                // Crossbar
                ctx.fillRect(x - 15, groundY - pole.height + 10, 30, 3);
                
                // Insulators
                ctx.fillStyle = '#8a7a6a';
                ctx.beginPath();
                ctx.arc(x - 12, groundY - pole.height + 18, 3, 0, Math.PI * 2);
                ctx.arc(x + 12, groundY - pole.height + 18, 3, 0, Math.PI * 2);
                ctx.fill();
            });
        }
        
        // Draw road
        function drawRoad() {
            const groundY = canvas.height * 0.78;
            const roadHeight = canvas.height * 0.22;
            
            // Ground
            ctx.fillStyle = '#4a6741';
            ctx.fillRect(0, groundY, canvas.width, roadHeight);
            
            // Road surface
            ctx.fillStyle = '#3a3a3a';
            ctx.fillRect(0, groundY + 20, canvas.width, roadHeight - 20);
            
            // Road markings
            layers.ground.offset -= layers.ground.speed;
            if (layers.ground.offset < -100) layers.ground.offset = 0;
            
            ctx.fillStyle = '#e0e0e0';
            for (let x = layers.ground.offset; x < canvas.width; x += 100) {
                ctx.fillRect(x, groundY + roadHeight * 0.4, 50, 4);
            }
            
            // Road edge
            ctx.fillStyle = '#8a8a6a';
            ctx.fillRect(0, groundY + 15, canvas.width, 5);
        }
        
        // Draw car with detailed rendering
        function drawCar() {
            const carX = canvas.width * 0.35;
            const groundY = canvas.height * 0.78;
            const carY = groundY - 35 + car.bounce;
            
            // Car shadow
            ctx.fillStyle = 'rgba(0, 0, 0, 0.3)';
            ctx.beginPath();
            ctx.ellipse(carX, groundY - 5, 70, 10, 0, 0, Math.PI * 2);
            ctx.fill();
            
            // Car body - main chassis
            ctx.fillStyle = '#c0392b';
            ctx.beginPath();
            ctx.moveTo(carX - 80, carY);
            ctx.lineTo(carX - 70, carY - 35);
            ctx.lineTo(carX - 30, carY - 55);
            ctx.lineTo(carX + 20, carY - 55);
            ctx.lineTo(carX + 60, carY - 35);
            ctx.lineTo(carX + 75, carY - 15);
            ctx.lineTo(carX + 80, carY);
            ctx.closePath();
            ctx.fill();
            
            // Car body highlight
            ctx.fillStyle = '#d35400';
            ctx.beginPath();
            ctx.moveTo(carX - 65, carY - 5);
            ctx.lineTo(carX - 60, carY - 30);
            ctx.lineTo(carX - 30, carY - 48);
            ctx.lineTo(carX + 15, carY - 48);
            ctx.lineTo(carX + 50, carY - 32);
            ctx.lineTo(carX + 70, carY - 15);
            ctx.lineTo(carX + 75, carY - 5);
            ctx.closePath();
            ctx.fill();
            
            // Windows
            ctx.fillStyle = '#2c3e50';
            ctx.beginPath();
            ctx.moveTo(carX - 25, carY - 50);
            ctx.lineTo(carX + 15, carY - 50);
            ctx.lineTo(carX + 45, carY - 35);
            ctx.lineTo(carX - 20, carY - 35);
            ctx.closePath();
            ctx.fill();
            
            // Window reflection
            ctx.fillStyle = 'rgba(255, 255, 255, 0.2)';
            ctx.beginPath();
            ctx.moveTo(carX - 20, carY - 48);
            ctx.lineTo(carX - 5, carY - 48);
            ctx.lineTo(carX - 5, carY - 40);
            ctx.lineTo(carX - 20, carY - 40);
            ctx.closePath();
            ctx.fill();
            
            // Headlight
            ctx.fillStyle = '#f1c40f';
            ctx.beginPath();
            ctx.ellipse(carX + 75, carY - 12, 6, 8, 0, 0, Math.PI * 2);
            ctx.fill();
            
            // Headlight glow
            ctx.fillStyle = 'rgba(241, 196, 15, 0.3)';
            ctx.beginPath();
            ctx.moveTo(carX + 80, carY - 12);
            ctx.lineTo(carX + 200, carY - 30);
            ctx.lineTo(carX + 200, carY + 10);
            ctx.closePath();
            ctx.fill();
            
            // Taillight
            ctx.fillStyle = '#c0392b';
            ctx.fillRect(carX - 82, carY - 15, 4, 12);
            ctx.fillStyle = '#e74c3c';
            ctx.fillRect(carX - 81, carY - 14, 2, 10);
            
            // Door handle
            ctx.fillStyle = '#a0a0a0';
            ctx.fillRect(carX - 5, carY - 25, 12, 3);
            
            // Bumper front
            ctx.fillStyle = '#7f8c8d';
            ctx.fillRect(carX + 78, carY - 5, 5, 8);
            
            // Bumper rear
            ctx.fillRect(carX - 83, carY - 5, 5, 8);
            
            // Wheels
            drawWheel(carX - 45, carY + 5, 22);
            drawWheel(carX + 45, carY + 5, 22);
        }
        
        // Draw a wheel with rotation
        function drawWheel(x, y, radius) {
            ctx.save();
            ctx.translate(x, y);
            ctx.rotate(car.wheelRotation);
            
            // Tire
            ctx.fillStyle = '#1a1a1a';
            ctx.beginPath();
            ctx.arc(0, 0, radius, 0, Math.PI * 2);
            ctx.fill();
            
            // Tire sidewall
            ctx.fillStyle = '#2a2a2a';
            ctx.beginPath();
            ctx.arc(0, 0, radius - 3, 0, Math.PI * 2);
            ctx.fill();
            
            // Rim
            ctx.fillStyle = '#95a5a6';
            ctx.beginPath();
            ctx.arc(0, 0, radius - 8, 0, Math.PI * 2);
            ctx.fill();
            
            // Spokes
            ctx.strokeStyle = '#7f8c8d';
            ctx.lineWidth = 3;
            for (let i = 0; i < 5; i++) {
                ctx.beginPath();
                ctx.moveTo(0, 0);
                ctx.lineTo(
                    Math.cos(i * Math.PI * 2 / 5) * (radius - 10),
                    Math.sin(i * Math.PI * 2 / 5) * (radius - 10)
                );
                ctx.stroke();
            }
            
            // Hub
            ctx.fillStyle = '#34495e';
            ctx.beginPath();
            ctx.arc(0, 0, 5, 0, Math.PI * 2);
            ctx.fill();
            
            ctx.restore();
        }
        
        // Update physics and animation state
        function update() {
            time += 0.016;
            
            // Wheel rotation based on speed
            car.wheelRotation += speed * 0.05;
            
            // Car bounce simulation
            car.bouncePhase += 0.15;
            car.bounce = Math.sin(car.bouncePhase) * 1.5 + Math.sin(car.bouncePhase * 2.3) * 0.5;
        }
        
        // Main render loop
        function render() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            
            drawSky();
            drawMountains();
            drawHills();
            drawTrees();
            drawPoles();
            drawRoad();
            drawCar();
            
            update();
            requestAnimationFrame(render);
        }
        
        initScenery();
        render();
    </script>
</body>
</html>

Claude Sonnet 4.6

🥇 Best
16.0
TPS
34.6s
Duration
13.9KB
Output
3562
Tokens
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Driving</title>
<style>
  * { margin: 0; padding: 0; box-sizing: border-box; }
  body { overflow: hidden; background: #000; }
  canvas { display: block; }
</style>
</head>
<body>
<canvas id="c"></canvas>
<script>
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
let W, H;
function resize() { W = canvas.width = window.innerWidth; H = canvas.height = window.innerHeight; }
resize();
window.addEventListener('resize', resize);

const SPEED = 3.5;
let offset = 0;
let frame = 0;

function roadY() { return H * 0.64; }

// Pre-generate scenery
const rand = (a, b) => a + Math.random() * (b - a);

const mtns = Array.from({length: 30}, (_, i) => ({
  x: i * 220 + rand(0, 80),
  h: rand(70, 160), w: rand(140, 220),
  col: `hsl(230,${rand(15,30)}%,${rand(12,22)}%)`
}));

const hills = Array.from({length: 20}, (_, i) => ({
  x: i * 180 + rand(0, 60),
  h: rand(30, 70), w: rand(200, 350),
  col: `hsl(130,${rand(20,35)}%,${rand(10,18)}%)`
}));

const bgTrees = Array.from({length: 40}, (_, i) => ({
  x: i * 140 + rand(0, 60),
  h: rand(55, 90), type: Math.random() > 0.5 ? 'pine' : 'round'
}));

const poles = Array.from({length: 15}, (_, i) => ({
  x: i * 300 + rand(0, 120), h: rand(90, 130)
}));

const fgTrees = Array.from({length: 18}, (_, i) => ({
  x: i * 260 + rand(0, 100), h: rand(60, 95)
}));

const stars = Array.from({length: 80}, () => ({
  x: rand(0, 3000), y: rand(0, H * 0.45), r: rand(0.5, 1.8),
  phase: rand(0, Math.PI * 2)
}));

const clouds = Array.from({length: 12}, (_, i) => ({
  x: i * 350 + rand(0, 200), y: rand(20, 80),
  w: rand(120, 220), h: rand(30, 55)
}));

function drawSky() {
  const ry = roadY();
  const g = ctx.createLinearGradient(0, 0, 0, ry);
  g.addColorStop(0, '#05051a');
  g.addColorStop(0.25, '#0d1035');
  g.addColorStop(0.55, '#8b1a00');
  g.addColorStop(0.78, '#e85d00');
  g.addColorStop(0.92, '#f5a623');
  g.addColorStop(1, '#ffd580');
  ctx.fillStyle = g;
  ctx.fillRect(0, 0, W, ry);
}

function drawStars() {
  for (const s of stars) {
    const x = ((s.x - offset * 0.008) % (W + 200) + W + 200) % (W + 200) - 100;
    const alpha = 0.4 + 0.6 * Math.sin(frame * 0.04 + s.phase);
    ctx.fillStyle = `rgba(255,255,255,${alpha})`;
    ctx.beginPath();
    ctx.arc(x, s.y, s.r, 0, Math.PI * 2);
    ctx.fill();
  }
}

function drawSun() {
  const ry = roadY();
  const sx = W * 0.68, sy = ry * 0.82;
  const g = ctx.createRadialGradient(sx, sy, 0, sx, sy, 130);
  g.addColorStop(0, 'rgba(255,220,60,0.55)');
  g.addColorStop(0.4, 'rgba(255,120,0,0.2)');
  g.addColorStop(1, 'rgba(255,60,0,0)');
  ctx.fillStyle = g;
  ctx.fillRect(0, 0, W, ry);
  ctx.fillStyle = '#ffc830';
  ctx.beginPath();
  ctx.arc(sx, sy, 38, 0, Math.PI * 2);
  ctx.fill();
  ctx.fillStyle = '#ffe060';
  ctx.beginPath();
  ctx.arc(sx, sy, 28, 0, Math.PI * 2);
  ctx.fill();
}

function drawClouds() {
  for (const c of clouds) {
    const x = ((c.x - offset * 0.018) % (W + 500) + W + 500) % (W + 500) - 250;
    ctx.fillStyle = 'rgba(200,90,30,0.22)';
    const drawEllipse = (dx, dy, rx, ry2) => {
      ctx.beginPath(); ctx.ellipse(x + dx, c.y + dy, rx, ry2, 0, 0, Math.PI * 2); ctx.fill();
    };
    drawEllipse(0, 0, c.w * 0.45, c.h * 0.38);
    drawEllipse(-c.w * 0.28, c.h * 0.08, c.w * 0.28, c.h * 0.28);
    drawEllipse(c.w * 0.25, c.h * 0.1, c.w * 0.3, c.h * 0.25);
  }
}

function drawMountains() {
  const ry = roadY();
  for (const m of mtns) {
    const x = ((m.x - offset * 0.04) % (W + 500) + W + 500) % (W + 500) - 250;
    ctx.fillStyle = m.col;
    ctx.beginPath();
    ctx.moveTo(x - m.w * 0.5, ry);
    ctx.lineTo(x, ry - m.h);
    ctx.lineTo(x + m.w * 0.5, ry);
    ctx.closePath();
    ctx.fill();
    // Snow cap
    ctx.fillStyle = 'rgba(255,255,255,0.07)';
    ctx.beginPath();
    ctx.moveTo(x - m.w * 0.1, ry - m.h + m.h * 0.15);
    ctx.lineTo(x, ry - m.h);
    ctx.lineTo(x + m.w * 0.1, ry - m.h + m.h * 0.15);
    ctx.closePath();
    ctx.fill();
  }
}

function drawHillLayer() {
  const ry = roadY();
  // Wave hills
  ctx.fillStyle = '#142a0e';
  ctx.beginPath();
  ctx.moveTo(0, ry);
  for (let x = 0; x <= W; x += 6) {
    const sx = x + offset * 0.12;
    const y = ry - 28 - Math.sin(sx * 0.004) * 22 - Math.sin(sx * 0.0015) * 14;
    x === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
  }
  ctx.lineTo(W, ry); ctx.closePath(); ctx.fill();

  // Grassy edge top of road
  ctx.fillStyle = '#1d3d12';
  ctx.beginPath();
  ctx.moveTo(0, ry);
  for (let x = 0; x <= W; x += 6) {
    const sx = x + offset * 0.22;
    const y = ry - 8 - Math.sin(sx * 0.012) * 4;
    x === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
  }
  ctx.lineTo(W, ry); ctx.closePath(); ctx.fill();
}

function drawBgTrees() {
  const ry = roadY();
  for (const t of bgTrees) {
    const x = ((t.x - offset * 0.25) % (W + 400) + W + 400) % (W + 400) - 200;
    drawTree(x, ry - 8, t.h * 0.72, t.type, '#1a4020', '#0e2310');
  }
}

function drawTree(x, baseY, h, type, col1, col2) {
  if (type === 'pine') {
    ctx.fillStyle = col1;
    for (let tier = 0; tier < 3; tier++) {
      const ty = baseY - h * (0.3 * tier + 0.3);
      const spread = h * (0.32 - tier * 0.07);
      ctx.beginPath();
      ctx.moveTo(x, ty - h * 0.4);
      ctx.lineTo(x - spread, ty);
      ctx.lineTo(x + spread, ty);
      ctx.closePath(); ctx.fill();
    }
    ctx.fillStyle = col2;
    ctx.fillRect(x - 3, baseY - h * 0.32, 6, h * 0.32);
  } else {
    ctx.fillStyle = '#3d2800';
    const tw = 5, th = h * 0.32;
    ctx.fillRect(x - tw * 0.5, baseY - th, tw, th);
    ctx.fillStyle = col1;
    ctx.beginPath();
    ctx.arc(x, baseY - th - h * 0.38, h * 0.36, 0, Math.PI * 2);
    ctx.fill();
    ctx.fillStyle = col2;
    ctx.beginPath();
    ctx.arc(x - h * 0.1, baseY - th - h * 0.42, h * 0.2, 0, Math.PI * 2);
    ctx.fill();
  }
}

function drawRoad() {
  const ry = roadY();
  const rh = H - ry;
  const g = ctx.createLinearGradient(0, ry, 0, H);
  g.addColorStop(0, '#2e2e2e');
  g.addColorStop(1, '#484848');
  ctx.fillStyle = g;
  ctx.fillRect(0, ry, W, rh);

  // Shoulder lines
  ctx.fillStyle = '#f0f0b0';
  ctx.fillRect(0, ry + 2, W, 4);
  ctx.fillStyle = '#e0e0a0';
  ctx.fillRect(0, H - 6, W, 4);
}

function drawLaneMarkings() {
  const ry = roadY();
  const laneH = H - ry;
  const laneY = ry + laneH * 0.42;
  const mw = 55, mh = 7, gap = 110;
  ctx.fillStyle = '#e8e870';
  for (let i = -2; i < W / gap + 3; i++) {
    const x = ((i * gap - offset * 1) % (W + gap * 4) + W + gap * 4) % (W + gap * 4) - gap * 2;
    if (x < W + mw) ctx.fillRect(x, laneY - mh * 0.5, mw, mh);
  }
}

function drawPoles() {
  const ry = roadY();
  for (const p of poles) {
    const x = ((p.x - offset * 0.75) % (W + 600) + W + 600) % (W + 600) - 300;
    ctx.fillStyle = '#5a4a30';
    ctx.fillRect(x - 4, ry - p.h, 8, p.h + 6);
    // Cross-arm
    ctx.fillStyle = '#4a3a22';
    ctx.fillRect(x - 30, ry - p.h + 8, 60, 5);
    // Wires to next pole position
    ctx.strokeStyle = 'rgba(60,50,30,0.8)';
    ctx.lineWidth = 1.5;
    ctx.beginPath();
    ctx.moveTo(x + 30, ry - p.h + 10);
    ctx.bezierCurveTo(x + 130, ry - p.h + 26, x + 230, ry - p.h + 26, x + 330, ry - p.h + 10);
    ctx.stroke();
    ctx.beginPath();
    ctx.moveTo(x - 30, ry - p.h + 10);
    ctx.bezierCurveTo(x - 130, ry - p.h + 26, x - 230, ry - p.h + 26, x - 330, ry - p.h + 10);
    ctx.stroke();
  }
}

function drawFgTrees() {
  const ry = roadY();
  for (const t of fgTrees) {
    const x = ((t.x - offset * 0.9) % (W + 600) + W + 600) % (W + 600) - 300;
    drawTree(x, ry - 6, t.h, 'round', '#245c1a', '#153a0e');
  }
}

function drawCar(bob) {
  const ry = roadY();
  const cx = W * 0.32;
  const cy = ry + bob;
  const bw = 210, bh = 62;
  const wr = 30;
  const bx = cx - bw * 0.42;

  // Shadow
  const shg = ctx.createRadialGradient(cx, ry + 10, 5, cx, ry + 10, bw * 0.52);
  shg.addColorStop(0, 'rgba(0,0,0,0.45)');
  shg.addColorStop(1, 'rgba(0,0,0,0)');
  ctx.fillStyle = shg;
  ctx.beginPath();
  ctx.ellipse(cx, ry + 10, bw * 0.52, 10, 0, 0, Math.PI * 2);
  ctx.fill();

  // Lower body
  ctx.fillStyle = '#b02010';
  ctx.beginPath();
  ctx.moveTo(bx + 18, cy - wr * 0.4);
  ctx.lineTo(bx + 12, cy - bh * 0.6);
  ctx.lineTo(bx + bw * 0.08, cy - bh * 0.88);
  ctx.lineTo(bx + bw * 0.33, cy - bh * 1.08);
  ctx.lineTo(bx + bw * 0.72, cy - bh * 1.08);
  ctx.lineTo(bx + bw * 0.9, cy - bh * 0.68);
  ctx.lineTo(bx + bw - 6, cy - wr * 0.4);
  ctx.closePath();
  ctx.fill();

  // Body highlight
  ctx.fillStyle = 'rgba(255,80,40,0.25)';
  ctx.beginPath();
  ctx.moveTo(bx + 30, cy - bh * 0.65);
  ctx.lineTo(bx + bw * 0.1, cy - bh * 0.9);
  ctx.lineTo(bx + bw * 0.6, cy - bh * 0.95);
  ctx.lineTo(bx + bw * 0.72, cy - bh * 0.8);
  ctx.closePath();
  ctx.fill();

  // Roof
  ctx.fillStyle = '#901808';
  ctx.beginPath();
  ctx.moveTo(bx + bw * 0.19, cy - bh * 0.88);
  ctx.lineTo(bx + bw * 0.31, cy - bh * 1.52);
  ctx.lineTo(bx + bw * 0.7, cy - bh * 1.52);
  ctx.lineTo(bx + bw * 0.84, cy - bh * 0.88);
  ctx.closePath();
  ctx.fill();

  // Windshield (front)
  ctx.fillStyle = 'rgba(130,200,240,0.68)';
  ctx.beginPath();
  ctx.moveTo(bx + bw * 0.56, cy - bh * 0.9);
  ctx.lineTo(bx + bw * 0.64, cy - bh * 1.46);
  ctx.lineTo(bx + bw * 0.79, cy - bh * 1.46);
  ctx.lineTo(bx + bw * 0.82, cy - bh * 0.9);
  ctx.closePath();
  ctx.fill();
  // Windshield reflection
  ctx.fillStyle = 'rgba(255,255,255,0.18)';
  ctx.beginPath();
  ctx.moveTo(bx + bw * 0.57, cy - bh * 0.92);
  ctx.lineTo(bx + bw * 0.63, cy - bh * 1.42);
  ctx.lineTo(bx + bw * 0.67, cy - bh * 1.42);
  ctx.lineTo(bx + bw * 0.61, cy - bh * 0.92);
  ctx.closePath();
  ctx.fill();

  // Rear window
  ctx.fillStyle = 'rgba(110,180,220,0.6)';
  ctx.beginPath();
  ctx.moveTo(bx + bw * 0.21, cy - bh * 0.9);
  ctx.lineTo(bx + bw * 0.33, cy - bh * 1.46);
  ctx.lineTo(bx + bw * 0.54, cy - bh * 1.46);
  ctx.lineTo(bx + bw * 0.54, cy - bh * 0.9);
  ctx.closePath();
  ctx.fill();

  // Door seam
  ctx.strokeStyle = '#6a0c04';
  ctx.lineWidth = 1.8;
  ctx.beginPath();
  ctx.moveTo(bx + bw * 0.54, cy - wr * 0.4);
  ctx.lineTo(bx + bw * 0.54, cy - bh * 0.9);
  ctx.stroke();
  ctx.beginPath();
  ctx.moveTo(bx + bw * 0.54, cy - bh * 0.56);
  ctx.quadraticCurveTo(bx + bw * 0.37, cy - bh * 0.56, bx + bw * 0.28, cy - bh * 0.5);
  ctx.stroke();

  // Headlight
  ctx.fillStyle = '#ffe870';
  ctx.beginPath();
  ctx.ellipse(bx + bw - 7, cy - bh * 0.52, 10, 6, -0.15, 0, Math.PI * 2);
  ctx.fill();
  ctx.fillStyle = '#fff8c0';
  ctx.beginPath();
  ctx.ellipse(bx + bw - 8, cy - bh * 0.52, 6, 3.5, -0.15, 0, Math.PI * 2);
  ctx.fill();

  // Headlight beam
  const beam = ctx.createLinearGradient(bx + bw, cy - bh * 0.55, bx + bw + 260, cy - 5);
  beam.addColorStop(0, 'rgba(255,245,150,0.18)');
  beam.addColorStop(1, 'rgba(255,245,150,0)');
  ctx.fillStyle = beam;
  ctx.beginPath();
  ctx.moveTo(bx + bw - 4, cy - bh * 0.58);
  ctx.lineTo(bx + bw + 260, cy - bh * 0.35);
  ctx.lineTo(bx + bw + 260, cy - 2);
  ctx.lineTo(bx + bw - 4, cy - bh * 0.46);
  ctx.closePath();
  ctx.fill();

  // Tail light
  ctx.fillStyle = '#dd0000';
  ctx.beginPath();
  ctx.ellipse(bx + 10, cy - bh * 0.52, 7, 5, 0.1, 0, Math.PI * 2);
  ctx.fill();
  ctx.fillStyle = 'rgba(255,80,60,0.5)';
  ctx.beginPath();
  ctx.ellipse(bx + 9, cy - bh * 0.52, 4, 3, 0.1, 0, Math.PI * 2);
  ctx.fill();

  // Mirror
  ctx.fillStyle = '#880e06';
  ctx.beginPath();
  ctx.moveTo(bx + bw * 0.82, cy - bh * 0.9);
  ctx.lineTo(bx + bw * 0.9, cy - bh * 1.02);
  ctx.lineTo(bx + bw * 0.96, cy - bh * 0.98);
  ctx.lineTo(bx + bw * 0.86, cy - bh * 0.86);
  ctx.closePath();
  ctx.fill();

  // Wheels
  const fwx = bx + bw * 0.78, rwx = bx + bw * 0.2;
  const wheelY = cy;
  drawWheel(fwx, wheelY, wr);
  drawWheel(rwx, wheelY, wr);

  // Undercarriage
  ctx.fillStyle = '#444';
  ctx.fillRect(bx + 28, cy - 7, bw - 55, 7);

  // Bumpers
  ctx.fillStyle = '#6a0c04';
  ctx.beginPath();
  ctx.moveTo(bx + bw - 5, cy - bh * 0.4);
  ctx.lineTo(bx + bw + 8, cy - bh * 0.3);
  ctx.lineTo(bx + bw + 8, cy - wr * 0.3);
  ctx.lineTo(bx + bw - 5, cy - wr * 0.3);
  ctx.closePath();
  ctx.fill();
  ctx.beginPath();
  ctx.moveTo(bx + 18, cy - bh * 0.4);
  ctx.lineTo(bx + 6, cy - bh * 0.3);
  ctx.lineTo(bx + 6, cy - wr * 0.3);
  ctx.lineTo(bx + 18, cy - wr * 0.3);
  ctx.closePath();
  ctx.fill();
}

function drawWheel(cx, cy, r) {
  const angle = (frame * SPEED) / r;

  // Tire
  ctx.fillStyle = '#181818';
  ctx.beginPath(); ctx.arc(cx, cy, r, 0, Math.PI * 2); ctx.fill();
  // Tire wall highlight
  ctx.strokeStyle = '#303030';
  ctx.lineWidth = 4;
  ctx.beginPath(); ctx.arc(cx, cy, r - 2, 0, Math.PI * 2); ctx.stroke();

  // Rim
  ctx.fillStyle = '#c8c8c8';
  ctx.beginPath(); ctx.arc(cx, cy, r * 0.62, 0, Math.PI * 2); ctx.fill();
  ctx.fillStyle = '#b0b0b0';
  ctx.beginPath(); ctx.arc(cx, cy, r * 0.52, 0, Math.PI * 2); ctx.fill();

  // Spokes
  const nSpokes = 5;
  ctx.lineWidth = 3.5;
  for (let i = 0; i < nSpokes; i++) {
    const a = angle + (i / nSpokes) * Math.PI * 2;
    const ix = cx + Math.cos(a) * r * 0.52;
    const iy = cy + Math.sin(a) * r * 0.52;
    const grad = ctx.createLinearGradient(cx, cy, ix, iy);
    grad.addColorStop(0, '#aaa');
    grad.addColorStop(1, '#888');
    ctx.strokeStyle = grad;
    ctx.beginPath(); ctx.moveTo(cx, cy); ctx.lineTo(ix, iy); ctx.stroke();
  }

  // Lug nuts
  for (let i = 0; i < nSpokes; i++) {
    const a = angle + (i / nSpokes) * Math.PI * 2;
    const nx = cx + Math.cos(a) * r * 0.37;
    const ny = cy + Math.sin(a) * r * 0.37;
    ctx.fillStyle = '#666';
    ctx.beginPath(); ctx.arc(nx, ny, 3.5, 0, Math.PI * 2); ctx.fill();
  }

  // Center cap
  ctx.fillStyle = '#888';
  ctx.beginPath(); ctx.arc(cx, cy, r * 0.14, 0, Math.PI * 2); ctx.fill();
  ctx.fillStyle = '#aaa';
  ctx.beginPath(); ctx.arc(cx, cy, r * 0.08, 0, Math.PI * 2); ctx.fill();
}

function loop() {
  ctx.clearRect(0, 0, W, H);
  offset += SPEED;
  frame++;

  const bob = Math.sin(frame * 0.09) * 1.4 + Math.sin(frame * 0.15) * 0.7;

  drawSky();
  drawStars();
  drawSun();
  drawClouds();
  drawMountains();
  drawHillLayer();
  drawBgTrees();
  drawRoad();
  drawLaneMarkings();
  drawPoles();
  drawFgTrees();
  drawCar(bob);

  requestAnimationFrame(loop);
}

loop();
</script>
</body>
</html>

GPT-5.4 Mini

👑 Premium
45.8
TPS
1.4m
Duration
15.4KB
Output
3947
Tokens
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Driving Scene</title>
  <style>
    html, body {
      width: 100%;
      height: 100%;
      margin: 0;
      overflow: hidden;
      background: #090b14;
    }

    canvas {
      display: block;
      width: 100vw;
      height: 100vh;
    }
  </style>
</head>
<body>
<canvas id="scene"></canvas>
<script>
(() => {
  const canvas = document.getElementById('scene');
  const ctx = canvas.getContext('2d');
  const DPR = Math.max(1, Math.min(2, window.devicePixelRatio || 1));

  let W = 0;
  let H = 0;
  let roadY = 0;
  let worldLength = 2400;
  let props = [];
  let last = performance.now();

  const rand = (n) => {
    const x = Math.sin(n * 999.123 + 7.77) * 43758.5453;
    return x - Math.floor(x);
  };

  function resize() {
    W = Math.max(1, window.innerWidth);
    H = Math.max(1, window.innerHeight);
    canvas.width = Math.round(W * DPR);
    canvas.height = Math.round(H * DPR);
    canvas.style.width = W + 'px';
    canvas.style.height = H + 'px';
    ctx.setTransform(DPR, 0, 0, DPR, 0, 0);
    roadY = H * 0.78;
    worldLength = Math.max(1800, Math.round(W * 2.2));
    buildProps();
  }

  function buildProps() {
    props = [];
    for (let i = 0; i < 150; i++) {
      const x = rand(i * 11.3) * worldLength;
      const typeSeed = rand(i * 29.7);
      const type = typeSeed < 0.38 ? 'tree' : typeSeed < 0.58 ? 'pole' : typeSeed < 0.8 ? 'bush' : 'sign';
      props.push({
        x,
        type,
        scale: 0.7 + rand(i * 5.1) * 1.5,
        tint: rand(i * 8.9)
      });
    }
  }

  const repeat = (value, length) => ((value % length) + length) % length;

  function skyGradient() {
    const g = ctx.createLinearGradient(0, 0, 0, H);
    g.addColorStop(0, '#08111f');
    g.addColorStop(0.38, '#18263d');
    g.addColorStop(0.68, '#4d4b72');
    g.addColorStop(1, '#f2a15d');
    return g;
  }

  function drawBackground(t) {
    ctx.fillStyle = skyGradient();
    ctx.fillRect(0, 0, W, H);

    const sunX = W * 0.78;
    const sunY = H * 0.25;
    const glow = ctx.createRadialGradient(sunX, sunY, 0, sunX, sunY, H * 0.35);
    glow.addColorStop(0, 'rgba(255,236,186,0.95)');
    glow.addColorStop(0.18, 'rgba(255,192,120,0.4)');
    glow.addColorStop(1, 'rgba(255,150,80,0)');
    ctx.fillStyle = glow;
    ctx.fillRect(0, 0, W, H);

    ctx.save();
    ctx.globalAlpha = 0.4;
    ctx.fillStyle = '#f7cda2';
    for (let i = 0; i < 3; i++) {
      const cx = W * (0.15 + i * 0.22) + Math.sin(t * 0.00005 + i) * 40;
      const cy = H * (0.12 + i * 0.04);
      const rw = W * (0.18 + i * 0.03);
      const rh = H * 0.035;
      ctx.beginPath();
      ctx.ellipse(cx, cy, rw, rh, -0.08, 0, Math.PI * 2);
      ctx.fill();
    }
    ctx.restore();
  }

  function drawMountains(t) {
    const base = H * 0.64;
    const layers = [
      { y: base + 42, h: 115, color: '#2c2f4c', speed: 0.02, scale: 0.65 },
      { y: base + 12, h: 150, color: '#3f456d', speed: 0.04, scale: 0.85 }
    ];

    for (const layer of layers) {
      ctx.fillStyle = layer.color;
      const offset = repeat(t * 0.005 * layer.speed * worldLength, worldLength);
      for (let x = -worldLength; x < W + worldLength; x += worldLength / 8) {
        const px = x - offset * layer.scale;
        const peak = 40 + rand((x + layer.h) * 0.01) * layer.h;
        ctx.beginPath();
        ctx.moveTo(px, layer.y);
        ctx.quadraticCurveTo(px + worldLength * 0.12, layer.y - peak * 0.45, px + worldLength * 0.24, layer.y);
        ctx.quadraticCurveTo(px + worldLength * 0.34, layer.y - peak, px + worldLength * 0.5, layer.y - peak * 0.1);
        ctx.quadraticCurveTo(px + worldLength * 0.63, layer.y - peak * 0.55, px + worldLength * 0.82, layer.y);
        ctx.lineTo(px + worldLength * 0.82, H);
        ctx.lineTo(px, H);
        ctx.closePath();
        ctx.fill();
      }
    }
  }

  function drawRoadAndGround(t) {
    const horizon = H * 0.67;
    const roadTop = H * 0.77;
    const ground = ctx.createLinearGradient(0, horizon, 0, H);
    ground.addColorStop(0, '#3a3b33');
    ground.addColorStop(0.45, '#2c2c28');
    ground.addColorStop(1, '#151614');
    ctx.fillStyle = ground;
    ctx.fillRect(0, roadTop, W, H - roadTop);

    ctx.fillStyle = '#22231e';
    ctx.fillRect(0, roadTop - 14, W, 16);

    ctx.fillStyle = '#4c4636';
    ctx.fillRect(0, roadTop + 10, W, 24);

    const laneOffset = repeat(t * 0.16 * worldLength, 220);
    for (let x = -220; x < W + 220; x += 220) {
      const px = x - laneOffset;
      const w = 88;
      const h = 7;
      ctx.fillStyle = 'rgba(255,233,160,0.9)';
      ctx.fillRect(px, roadTop + 19, w, h);
    }

    ctx.fillStyle = '#1e1d18';
    ctx.fillRect(0, roadTop + 36, W, H - roadTop - 36);

    const grassOffset = repeat(t * 0.28 * worldLength, 120);
    for (let x = -120; x < W + 120; x += 120) {
      const px = x - grassOffset;
      const s = 0.7 + rand((x + 3) * 0.2) * 0.8;
      ctx.strokeStyle = `rgba(${50 + s * 20}, ${90 + s * 60}, ${40 + s * 18}, 0.45)`;
      ctx.lineWidth = 2;
      ctx.beginPath();
      ctx.moveTo(px, roadTop + 40);
      ctx.quadraticCurveTo(px + 18 * s, roadTop + 18 - 10 * s, px + 30 * s, roadTop + 34);
      ctx.stroke();
    }
  }

  function drawScenery(t) {
    const roadTop = H * 0.77;
    const layerSpeeds = {
      tree: 0.36,
      pole: 0.58,
      bush: 0.8,
      sign: 0.66
    };

    for (const item of props) {
      const speed = layerSpeeds[item.type] || 0.5;
      const px = repeat(item.x - t * 0.16 * speed * worldLength, worldLength);
      const x = px - worldLength * 0.1;
      const screenX = x;
      const y = roadTop + 8;
      const scale = item.scale;
      if (item.type === 'tree') {
        const trunkH = 34 * scale;
        const trunkW = 8 * scale;
        const crown = 38 * scale;
        ctx.fillStyle = `rgba(68,43,28,0.95)`;
        ctx.fillRect(screenX, y - trunkH, trunkW, trunkH);
        ctx.fillStyle = `rgba(${28 + item.tint * 16}, ${72 + item.tint * 40}, ${34 + item.tint * 24}, 0.97)`;
        for (let i = 0; i < 3; i++) {
          ctx.beginPath();
          ctx.ellipse(screenX + trunkW * 0.5 + (i - 1) * 14 * scale, y - trunkH - 10 * scale, crown * (0.55 + i * 0.1), crown * (0.42 + i * 0.1), 0, 0, Math.PI * 2);
          ctx.fill();
        }
      } else if (item.type === 'pole') {
        ctx.fillStyle = '#6f7080';
        ctx.fillRect(screenX, y - 92 * scale, 6 * scale, 92 * scale);
        ctx.strokeStyle = '#bbb8aa';
        ctx.lineWidth = 3 * scale;
        ctx.beginPath();
        ctx.moveTo(screenX + 3 * scale, y - 88 * scale);
        ctx.lineTo(screenX + 34 * scale, y - 78 * scale);
        ctx.stroke();
      } else if (item.type === 'sign') {
        ctx.fillStyle = '#5f5f66';
        ctx.fillRect(screenX, y - 70 * scale, 6 * scale, 70 * scale);
        ctx.fillStyle = '#2b2f40';
        ctx.fillRect(screenX - 10 * scale, y - 98 * scale, 52 * scale, 28 * scale);
        ctx.strokeStyle = 'rgba(255,255,255,0.25)';
        ctx.lineWidth = 2;
        ctx.strokeRect(screenX - 10 * scale, y - 98 * scale, 52 * scale, 28 * scale);
      } else {
        ctx.fillStyle = `rgba(${40 + item.tint * 20}, ${72 + item.tint * 36}, ${34 + item.tint * 12}, 0.9)`;
        ctx.beginPath();
        ctx.ellipse(screenX, y - 10 * scale, 18 * scale, 7 * scale, 0.2, 0, Math.PI * 2);
        ctx.fill();
      }
    }

    // Foreground streaks for motion depth.
    const streakOffset = repeat(t * 0.35 * worldLength, 70);
    ctx.strokeStyle = 'rgba(231,207,153,0.14)';
    ctx.lineWidth = 3;
    for (let x = -70; x < W + 70; x += 70) {
      const px = x - streakOffset;
      ctx.beginPath();
      ctx.moveTo(px, roadTop + 68);
      ctx.lineTo(px + 20, roadTop + 72);
      ctx.stroke();
    }
  }

  function drawWheel(x, y, r, angle, hubColor, tireColor) {
    ctx.save();
    ctx.translate(x, y);
    ctx.rotate(angle);

    ctx.fillStyle = tireColor;
    ctx.beginPath();
    ctx.arc(0, 0, r, 0, Math.PI * 2);
    ctx.fill();

    ctx.fillStyle = '#1d1d1f';
    ctx.beginPath();
    ctx.arc(0, 0, r * 0.78, 0, Math.PI * 2);
    ctx.fill();

    ctx.strokeStyle = 'rgba(220,220,225,0.82)';
    ctx.lineWidth = Math.max(1.5, r * 0.12);
    for (let i = 0; i < 6; i++) {
      ctx.beginPath();
      ctx.moveTo(0, 0);
      ctx.lineTo(0, -r * 0.67);
      ctx.stroke();
      ctx.rotate(Math.PI / 3);
    }

    ctx.fillStyle = hubColor;
    ctx.beginPath();
    ctx.arc(0, 0, r * 0.28, 0, Math.PI * 2);
    ctx.fill();

    ctx.restore();
  }

  function drawCar(t) {
    const speed = 0.0062;
    const bob = Math.sin(t * 0.0028) * 2.5 + Math.sin(t * 0.007) * 0.9;
    const pitch = Math.sin(t * 0.0032 + 1.5) * 0.02;
    const wheelSpin = t * speed * 3.9;
    const baseX = W * 0.31;
    const baseY = H * 0.72 + bob;
    const scale = Math.min(W, H) / 980;
    const bodyW = 300 * scale;
    const bodyH = 92 * scale;
    const wheelR = 33 * scale;
    const wheelY = baseY + 42 * scale;
    const wheelFrontX = baseX + 180 * scale;
    const wheelRearX = baseX + 76 * scale;

    // shadow
    ctx.save();
    ctx.translate(baseX + 132 * scale, baseY + 56 * scale);
    ctx.scale(1.25, 0.42);
    const shadow = ctx.createRadialGradient(0, 0, 0, 0, 0, 110 * scale);
    shadow.addColorStop(0, 'rgba(0,0,0,0.34)');
    shadow.addColorStop(1, 'rgba(0,0,0,0)');
    ctx.fillStyle = shadow;
    ctx.beginPath();
    ctx.arc(0, 0, 110 * scale, 0, Math.PI * 2);
    ctx.fill();
    ctx.restore();

    ctx.save();
    ctx.translate(baseX, baseY);
    ctx.rotate(pitch);

    // body
    const bodyGrad = ctx.createLinearGradient(0, -bodyH * 0.6, 0, bodyH);
    bodyGrad.addColorStop(0, '#f6d7a8');
    bodyGrad.addColorStop(0.24, '#d98b4a');
    bodyGrad.addColorStop(0.55, '#a54f24');
    bodyGrad.addColorStop(1, '#6b2b17');
    ctx.fillStyle = bodyGrad;

    ctx.beginPath();
    ctx.moveTo(0, 0);
    ctx.quadraticCurveTo(18 * scale, -18 * scale, 40 * scale, -32 * scale);
    ctx.quadraticCurveTo(74 * scale, -68 * scale, 140 * scale, -72 * scale);
    ctx.quadraticCurveTo(202 * scale, -74 * scale, 246 * scale, -48 * scale);
    ctx.quadraticCurveTo(288 * scale, -24 * scale, 300 * scale, 0);
    ctx.quadraticCurveTo(304 * scale, 14 * scale, 292 * scale, 22 * scale);
    ctx.quadraticCurveTo(276 * scale, 31 * scale, 250 * scale, 32 * scale);
    ctx.quadraticCurveTo(172 * scale, 36 * scale, 92 * scale, 36 * scale);
    ctx.quadraticCurveTo(24 * scale, 34 * scale, 0, 16 * scale);
    ctx.closePath();
    ctx.fill();

    // lower skirt
    ctx.fillStyle = 'rgba(34,21,20,0.9)';
    ctx.beginPath();
    ctx.roundRect(18 * scale, 20 * scale, 270 * scale, 18 * scale, 9 * scale);
    ctx.fill();

    // cabin
    ctx.fillStyle = 'rgba(247,248,255,0.86)';
    ctx.beginPath();
    ctx.moveTo(75 * scale, -34 * scale);
    ctx.quadraticCurveTo(92 * scale, -62 * scale, 126 * scale, -67 * scale);
    ctx.lineTo(183 * scale, -67 * scale);
    ctx.quadraticCurveTo(214 * scale, -66 * scale, 233 * scale, -41 * scale);
    ctx.lineTo(237 * scale, -4 * scale);
    ctx.lineTo(82 * scale, -4 * scale);
    ctx.closePath();
    ctx.fill();

    // windows
    ctx.fillStyle = 'rgba(28,44,66,0.78)';
    ctx.beginPath();
    ctx.moveTo(93 * scale, -32 * scale);
    ctx.quadraticCurveTo(104 * scale, -55 * scale, 128 * scale, -58 * scale);
    ctx.lineTo(175 * scale, -58 * scale);
    ctx.quadraticCurveTo(200 * scale, -57 * scale, 213 * scale, -39 * scale);
    ctx.lineTo(217 * scale, -10 * scale);
    ctx.lineTo(93 * scale, -10 * scale);
    ctx.closePath();
    ctx.fill();

    ctx.fillStyle = 'rgba(233,245,255,0.28)';
    ctx.fillRect(100 * scale, -47 * scale, 35 * scale, 5 * scale);
    ctx.fillRect(141 * scale, -47 * scale, 56 * scale, 5 * scale);

    // door line and highlights
    ctx.strokeStyle = 'rgba(255,240,214,0.55)';
    ctx.lineWidth = 1.2 * scale;
    ctx.beginPath();
    ctx.moveTo(167 * scale, -6 * scale);
    ctx.lineTo(170 * scale, -55 * scale);
    ctx.stroke();

    ctx.strokeStyle = 'rgba(255,255,255,0.2)';
    ctx.lineWidth = 2 * scale;
    ctx.beginPath();
    ctx.moveTo(48 * scale, -17 * scale);
    ctx.quadraticCurveTo(120 * scale, -57 * scale, 212 * scale, -48 * scale);
    ctx.stroke();

    ctx.strokeStyle = 'rgba(255,246,228,0.16)';
    ctx.lineWidth = 3 * scale;
    ctx.beginPath();
    ctx.moveTo(42 * scale, 2 * scale);
    ctx.lineTo(250 * scale, -2 * scale);
    ctx.stroke();

    // mirrors and details
    ctx.fillStyle = '#4b2a21';
    ctx.fillRect(196 * scale, -16 * scale, 12 * scale, 7 * scale);
    ctx.fillStyle = 'rgba(255,217,157,0.9)';
    ctx.fillRect(18 * scale, 8 * scale, 12 * scale, 4 * scale);
    ctx.fillRect(274 * scale, 6 * scale, 12 * scale, 4 * scale);

    // wheel wells
    ctx.fillStyle = '#2a1816';
    ctx.beginPath();
    ctx.arc(76 * scale, 36 * scale, 39 * scale, Math.PI, 0);
    ctx.fill();
    ctx.beginPath();
    ctx.arc(180 * scale, 36 * scale, 39 * scale, Math.PI, 0);
    ctx.fill();

    // ground contact / suspension
    ctx.strokeStyle = 'rgba(13,9,10,0.68)';
    ctx.lineWidth = 6 * scale;
    ctx.beginPath();
    ctx.moveTo(56 * scale, 34 * scale);
    ctx.lineTo(76 * scale, 38 * scale);
    ctx.lineTo(181 * scale, 38 * scale);
    ctx.lineTo(206 * scale, 34 * scale);
    ctx.stroke();

    // wheels
    drawWheel(wheelRearX - baseX, wheelY - baseY, wheelR, wheelSpin, '#b8b7b0', '#171719');
    drawWheel(wheelFrontX - baseX, wheelY - baseY, wheelR, wheelSpin * 1.03, '#b8b7b0', '#171719');

    // headlight glow and taillight
    const headGlow = ctx.createRadialGradient(290 * scale, 0, 0, 290 * scale, 0, 72 * scale);
    headGlow.addColorStop(0, 'rgba(255,236,166,0.95)');
    headGlow.addColorStop(1, 'rgba(255,236,166,0)');
    ctx.fillStyle = headGlow;
    ctx.beginPath();
    ctx.arc(290 * scale, 2 * scale, 72 * scale, -0.2, 0.45);
    ctx.fill();

    ctx.fillStyle = 'rgba(255,103,80,0.92)';
    ctx.fillRect(0, 8 * scale, 10 * scale, 8 * scale);

    ctx.restore();

    // motion blur streaks near wheels
    ctx.strokeStyle = 'rgba(255,231,195,0.07)';
    ctx.lineWidth = 5;
    ctx.beginPath();
    ctx.moveTo(baseX - 24, wheelY + 14 * scale);
    ctx.lineTo(baseX - 110, wheelY + 12 * scale);
    ctx.stroke();
  }

  function draw(t) {
    ctx.clearRect(0, 0, W, H);
    drawBackground(t);
    drawMountains(t);
    drawRoadAndGround(t);
    drawScenery(t);
    drawCar(t);

    // subtle atmospheric vignette
    const vignette = ctx.createRadialGradient(W * 0.5, H * 0.5, H * 0.2, W * 0.5, H * 0.5, Math.max(W, H) * 0.75);
    vignette.addColorStop(0, 'rgba(0,0,0,0)');
    vignette.addColorStop(1, 'rgba(0,0,0,0.24)');
    ctx.fillStyle = vignette;
    ctx.fillRect(0, 0, W, H);
  }

  function loop(now) {
    const dt = Math.min(32, now - last);
    last = now;
    draw(now);
    requestAnimationFrame(loop);
  }

  window.addEventListener('resize', resize, { passive: true });
  if (typeof CanvasRenderingContext2D !== 'undefined' && CanvasRenderingContext2D.prototype.roundRect === undefined) {
    CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) {
      const radius = Array.isArray(r) ? r : [r, r, r, r];
      this.beginPath();
      this.moveTo(x + radius[0], y);
      this.lineTo(x + w - radius[1], y);
      this.quadraticCurveTo(x + w, y, x + w, y + radius[1]);
      this.lineTo(x + w, y + h - radius[2]);
      this.quadraticCurveTo(x + w, y + h, x + w - radius[2], y + h);
      this.lineTo(x + radius[3], y + h);
      this.quadraticCurveTo(x, y + h, x, y + h - radius[3]);
      this.lineTo(x, y + radius[0]);
      this.quadraticCurveTo(x, y, x + radius[0], y);
      this.closePath();
    };
  }

  resize();
  requestAnimationFrame(loop);
})();
</script>
</body>
</html>

GPT-5.4

👑 Premium
26.3
TPS
3.2m
Duration
19.6KB
Output
5021
Tokens
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Driving at Dusk</title>
  <style>
    html, body {
      margin: 0;
      width: 100%;
      height: 100%;
      overflow: hidden;
      background: #0b1220;
    }

    body {
      font-family: Arial, sans-serif;
    }

    canvas {
      display: block;
      width: 100vw;
      height: 100vh;
    }
  </style>
</head>
<body>
  <canvas id="scene" aria-label="Animated side view of a car driving through a scrolling landscape"></canvas>
  <script>
    (() => {
      const canvas = document.getElementById('scene');
      const ctx = canvas.getContext('2d');

      const DPR = () => Math.min(window.devicePixelRatio || 1, 2);

      let width = 0;
      let height = 0;
      let horizon = 0;
      let roadTop = 0;
      let roadHeight = 0;
      let centerY = 0;

      const state = {
        speed: 260,
        start: performance.now()
      };

      function resize() {
        const dpr = DPR();
        width = window.innerWidth;
        height = window.innerHeight;
        canvas.width = Math.round(width * dpr);
        canvas.height = Math.round(height * dpr);
        canvas.style.width = width + 'px';
        canvas.style.height = height + 'px';
        ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
        horizon = height * 0.48;
        roadTop = height * 0.72;
        roadHeight = height - roadTop;
        centerY = roadTop + roadHeight * 0.28;
      }

      function hash(n) {
        const x = Math.sin(n * 127.1 + 311.7) * 43758.5453123;
        return x - Math.floor(x);
      }

      function lerp(a, b, t) {
        return a + (b - a) * t;
      }

      function mod(value, size) {
        return ((value % size) + size) % size;
      }

      function drawSky(time) {
        const sky = ctx.createLinearGradient(0, 0, 0, height);
        sky.addColorStop(0, '#12203a');
        sky.addColorStop(0.24, '#30466c');
        sky.addColorStop(0.52, '#ff9f6e');
        sky.addColorStop(0.7, '#f5c784');
        sky.addColorStop(1, '#f0d6a8');
        ctx.fillStyle = sky;
        ctx.fillRect(0, 0, width, height);

        const sunX = width * 0.78;
        const sunY = horizon - height * 0.15;
        const sunGlow = ctx.createRadialGradient(sunX, sunY, 0, sunX, sunY, height * 0.35);
        sunGlow.addColorStop(0, 'rgba(255, 243, 205, 0.95)');
        sunGlow.addColorStop(0.25, 'rgba(255, 188, 126, 0.45)');
        sunGlow.addColorStop(0.7, 'rgba(255, 136, 82, 0.12)');
        sunGlow.addColorStop(1, 'rgba(255, 136, 82, 0)');
        ctx.fillStyle = sunGlow;
        ctx.fillRect(0, 0, width, height);

        const haze = ctx.createLinearGradient(0, horizon - height * 0.06, 0, roadTop);
        haze.addColorStop(0, 'rgba(255, 216, 177, 0.25)');
        haze.addColorStop(1, 'rgba(255, 216, 177, 0)');
        ctx.fillStyle = haze;
        ctx.fillRect(0, horizon - height * 0.06, width, roadTop - horizon + height * 0.06);

        drawCloudBand(time);
      }

      function drawCloudBand(time) {
        const spacing = 260;
        const offset = mod(time * 3, spacing);
        for (let i = -2; i < Math.ceil(width / spacing) + 3; i++) {
          const idx = i + 100;
          const x = i * spacing - offset;
          const y = height * (0.14 + hash(idx) * 0.16);
          const w = 130 + hash(idx + 1) * 120;
          const h = 34 + hash(idx + 2) * 24;
          ctx.fillStyle = 'rgba(255,255,255,0.11)';
          blob(x, y, w, h, 5);
          ctx.fillStyle = 'rgba(255,220,198,0.07)';
          blob(x + 16, y + 6, w * 0.8, h * 0.7, 4);
        }
      }

      function blob(x, y, w, h, lobes) {
        ctx.beginPath();
        for (let i = 0; i < lobes; i++) {
          const px = x + (i / Math.max(1, lobes - 1)) * w;
          const py = y + Math.sin(i * 0.9) * h * 0.05;
          const r = lerp(h * 0.35, h * 0.6, i / Math.max(1, lobes - 1));
          ctx.moveTo(px + r, py);
          ctx.arc(px, py, r, 0, Math.PI * 2);
        }
        ctx.fill();
      }

      function drawMountainLayer(time, baseY, amplitude, colorA, colorB, speed, segment, roughness, alpha) {
        const offset = mod(time * speed, segment);
        const start = -segment;
        const end = width + segment * 2;
        const grad = ctx.createLinearGradient(0, baseY - amplitude * 1.2, 0, baseY + amplitude * 0.6);
        grad.addColorStop(0, colorA);
        grad.addColorStop(1, colorB);
        ctx.fillStyle = grad;
        ctx.globalAlpha = alpha;
        ctx.beginPath();
        ctx.moveTo(start, height);
        for (let x = start; x <= end; x += segment / 3) {
          const worldX = x + offset;
          const section = Math.floor(worldX / segment);
          const local = mod(worldX, segment) / segment;
          const peak = hash(section * 5 + 1) * 0.9 + 0.1;
          const valley = hash(section * 5 + 2) * 0.7;
          const sharp = Math.pow(Math.sin(local * Math.PI), lerp(0.8, 1.8, peak));
          const profile = sharp * (0.65 + peak * 0.75) + Math.sin(local * Math.PI * 2) * 0.08 * valley;
          const y = baseY - profile * amplitude * roughness;
          ctx.lineTo(x, y);
        }
        ctx.lineTo(end, height);
        ctx.closePath();
        ctx.fill();
        ctx.globalAlpha = 1;
      }

      function drawGroundLayers(time) {
        drawMountainLayer(time, horizon + height * 0.035, height * 0.19, '#6f5c7e', '#4b425f', 10, 220, 1.1, 0.55);
        drawMountainLayer(time, horizon + height * 0.07, height * 0.12, '#8b6c6f', '#64515b', 18, 170, 1.05, 0.7);
        drawMountainLayer(time, horizon + height * 0.11, height * 0.08, '#93755d', '#755b4d', 26, 130, 0.95, 0.9);

        const fieldGrad = ctx.createLinearGradient(0, horizon, 0, roadTop);
        fieldGrad.addColorStop(0, '#806652');
        fieldGrad.addColorStop(0.45, '#5b6b46');
        fieldGrad.addColorStop(1, '#36422f');
        ctx.fillStyle = fieldGrad;
        ctx.fillRect(0, horizon, width, roadTop - horizon);

        const fieldGlow = ctx.createLinearGradient(0, horizon, 0, roadTop);
        fieldGlow.addColorStop(0, 'rgba(255,183,120,0.18)');
        fieldGlow.addColorStop(1, 'rgba(255,183,120,0)');
        ctx.fillStyle = fieldGlow;
        ctx.fillRect(0, horizon, width, roadTop - horizon);

        drawShrubs(time);
        drawTrees(time);
        drawPoles(time);
      }

      function drawShrubs(time) {
        const spacing = 72;
        const offset = mod(time * 120, spacing);
        for (let i = -2; i < Math.ceil(width / spacing) + 4; i++) {
          const idx = i + 2000;
          const x = i * spacing - offset;
          const y = roadTop - 8 + Math.sin(idx) * 1.5;
          const r = 10 + hash(idx) * 12;
          ctx.fillStyle = `rgba(${Math.round(35 + hash(idx + 1) * 25)}, ${Math.round(55 + hash(idx + 2) * 45)}, ${Math.round(28 + hash(idx + 3) * 18)}, 0.88)`;
          ctx.beginPath();
          ctx.ellipse(x, y, r * 1.8, r, 0, 0, Math.PI * 2);
          ctx.fill();
        }
      }

      function drawTrees(time) {
        const spacing = 180;
        const offset = mod(time * 75, spacing);
        for (let i = -2; i < Math.ceil(width / spacing) + 4; i++) {
          const idx = i + 1000;
          const x = i * spacing - offset;
          const h = 70 + hash(idx) * 70;
          const lean = (hash(idx + 1) - 0.5) * 8;
          const trunkW = 4 + hash(idx + 2) * 4;
          const baseY = roadTop - 10;

          ctx.strokeStyle = 'rgba(51, 34, 24, 0.8)';
          ctx.lineWidth = trunkW;
          ctx.lineCap = 'round';
          ctx.beginPath();
          ctx.moveTo(x, baseY);
          ctx.lineTo(x + lean, baseY - h * 0.72);
          ctx.stroke();

          const crownY = baseY - h * 0.78;
          const crownR = 22 + hash(idx + 3) * 22;
          const crown = ctx.createRadialGradient(x, crownY - crownR * 0.3, 2, x, crownY, crownR * 1.6);
          crown.addColorStop(0, 'rgba(104, 140, 84, 0.95)');
          crown.addColorStop(0.55, 'rgba(61, 92, 51, 0.95)');
          crown.addColorStop(1, 'rgba(34, 53, 33, 0)');
          ctx.fillStyle = crown;
          ctx.beginPath();
          ctx.arc(x, crownY, crownR, 0, Math.PI * 2);
          ctx.arc(x - crownR * 0.8, crownY + 5, crownR * 0.76, 0, Math.PI * 2);
          ctx.arc(x + crownR * 0.78, crownY + 8, crownR * 0.68, 0, Math.PI * 2);
          ctx.fill();
        }
      }

      function drawPoles(time) {
        const spacing = 230;
        const offset = mod(time * 150, spacing);
        const poleTop = roadTop - 120;
        ctx.strokeStyle = 'rgba(58, 46, 37, 0.7)';
        ctx.lineWidth = 3;
        for (let i = -2; i < Math.ceil(width / spacing) + 4; i++) {
          const idx = i + 3000;
          const x = i * spacing - offset;
          const h = 76 + hash(idx) * 34;
          ctx.beginPath();
          ctx.moveTo(x, roadTop - 3);
          ctx.lineTo(x, roadTop - h);
          ctx.stroke();
          ctx.beginPath();
          ctx.moveTo(x - 8, roadTop - h + 8);
          ctx.lineTo(x + 10, roadTop - h + 8);
          ctx.stroke();
        }

        ctx.strokeStyle = 'rgba(95, 76, 62, 0.32)';
        ctx.lineWidth = 1.5;
        ctx.beginPath();
        for (let i = -2; i < Math.ceil(width / spacing) + 4; i++) {
          const x = i * spacing - offset;
          const y = poleTop + Math.sin((i + time * 0.25) * 0.7) * 6;
          if (i === -2) {
            ctx.moveTo(x, y);
          } else {
            ctx.lineTo(x, y);
          }
        }
        ctx.stroke();
      }

      function drawRoad(time) {
        const shoulderGrad = ctx.createLinearGradient(0, roadTop - 20, 0, roadTop + roadHeight);
        shoulderGrad.addColorStop(0, '#4f4238');
        shoulderGrad.addColorStop(0.2, '#2e2b2d');
        shoulderGrad.addColorStop(1, '#15181d');
        ctx.fillStyle = shoulderGrad;
        ctx.fillRect(0, roadTop - 10, width, roadHeight + 10);

        const asphalt = ctx.createLinearGradient(0, roadTop, 0, height);
        asphalt.addColorStop(0, '#39383e');
        asphalt.addColorStop(0.55, '#23252b');
        asphalt.addColorStop(1, '#12151a');
        ctx.fillStyle = asphalt;
        ctx.fillRect(0, roadTop + 18, width, roadHeight - 18);

        ctx.fillStyle = 'rgba(255, 177, 108, 0.08)';
        ctx.fillRect(0, roadTop + 18, width, 16);

        const stripeY = roadTop + roadHeight * 0.53;
        const stripeSpacing = 150;
        const stripeOffset = mod(time * state.speed * 0.9, stripeSpacing);
        for (let i = -2; i < Math.ceil(width / stripeSpacing) + 4; i++) {
          const x = i * stripeSpacing - stripeOffset;
          ctx.fillStyle = 'rgba(251, 229, 166, 0.9)';
          ctx.fillRect(x, stripeY, 74, 6);
        }

        const shimmer = ctx.createLinearGradient(0, roadTop, 0, height);
        shimmer.addColorStop(0, 'rgba(255,255,255,0.05)');
        shimmer.addColorStop(0.5, 'rgba(255,255,255,0)');
        shimmer.addColorStop(1, 'rgba(0,0,0,0.18)');
        ctx.fillStyle = shimmer;
        ctx.fillRect(0, roadTop, width, roadHeight);
      }

      function roundRectPath(x, y, w, h, r) {
        const radius = Math.min(r, w * 0.5, h * 0.5);
        ctx.beginPath();
        ctx.moveTo(x + radius, y);
        ctx.lineTo(x + w - radius, y);
        ctx.quadraticCurveTo(x + w, y, x + w, y + radius);
        ctx.lineTo(x + w, y + h - radius);
        ctx.quadraticCurveTo(x + w, y + h, x + w - radius, y + h);
        ctx.lineTo(x + radius, y + h);
        ctx.quadraticCurveTo(x, y + h, x, y + h - radius);
        ctx.lineTo(x, y + radius);
        ctx.quadraticCurveTo(x, y, x + radius, y);
        ctx.closePath();
      }

      function drawWheel(x, y, r, rotation) {
        const tire = ctx.createRadialGradient(x - r * 0.18, y - r * 0.18, r * 0.2, x, y, r);
        tire.addColorStop(0, '#4f5157');
        tire.addColorStop(0.65, '#191b20');
        tire.addColorStop(1, '#060708');
        ctx.fillStyle = tire;
        ctx.beginPath();
        ctx.arc(x, y, r, 0, Math.PI * 2);
        ctx.fill();

        ctx.strokeStyle = 'rgba(255,255,255,0.08)';
        ctx.lineWidth = 2;
        ctx.beginPath();
        ctx.arc(x, y, r * 0.82, 0, Math.PI * 2);
        ctx.stroke();

        const rim = ctx.createRadialGradient(x - r * 0.12, y - r * 0.18, r * 0.1, x, y, r * 0.64);
        rim.addColorStop(0, '#e4ebf2');
        rim.addColorStop(0.4, '#b8c1ca');
        rim.addColorStop(1, '#5c6470');
        ctx.fillStyle = rim;
        ctx.beginPath();
        ctx.arc(x, y, r * 0.58, 0, Math.PI * 2);
        ctx.fill();

        ctx.save();
        ctx.translate(x, y);
        ctx.rotate(rotation);
        ctx.strokeStyle = 'rgba(65, 70, 78, 0.9)';
        ctx.lineWidth = 3;
        for (let i = 0; i < 6; i++) {
          ctx.rotate(Math.PI / 3);
          ctx.beginPath();
          ctx.moveTo(0, 0);
          ctx.lineTo(r * 0.45, 0);
          ctx.stroke();
        }
        ctx.restore();

        ctx.fillStyle = '#4b525c';
        ctx.beginPath();
        ctx.arc(x, y, r * 0.14, 0, Math.PI * 2);
        ctx.fill();
      }

      function drawCar(time) {
        const carWidth = Math.min(width * 0.34, 420);
        const carHeight = carWidth * 0.25;
        const carX = width * 0.31;
        const wheelR = carWidth * 0.11;
        const bounce = Math.sin(time * 7.2) * 1.7 + Math.sin(time * 2.3) * 0.9;
        const pitch = Math.sin(time * 3.6) * 0.004;
        const baseY = roadTop + 14 + bounce;
        const wheelY = baseY + carHeight * 0.62;
        const frontWheelX = carX + carWidth * 0.72;
        const rearWheelX = carX + carWidth * 0.23;
        const bodyY = wheelY - wheelR * 1.55;
        const rotation = -time * state.speed / (wheelR * 6.2);

        const shadow = ctx.createRadialGradient(carX + carWidth * 0.48, wheelY + wheelR * 1.05, wheelR * 0.35, carX + carWidth * 0.48, wheelY + wheelR * 1.05, carWidth * 0.45);
        shadow.addColorStop(0, 'rgba(0,0,0,0.28)');
        shadow.addColorStop(1, 'rgba(0,0,0,0)');
        ctx.fillStyle = shadow;
        ctx.beginPath();
        ctx.ellipse(carX + carWidth * 0.48, wheelY + wheelR * 1.02, carWidth * 0.34, wheelR * 0.7, 0, 0, Math.PI * 2);
        ctx.fill();

        ctx.save();
        ctx.translate(carX + carWidth * 0.5, bodyY + carHeight * 0.55);
        ctx.rotate(pitch);
        ctx.translate(-(carX + carWidth * 0.5), -(bodyY + carHeight * 0.55));

        ctx.fillStyle = '#0f1116';
        ctx.beginPath();
        ctx.arc(rearWheelX, wheelY, wheelR * 1.28, Math.PI, 0, false);
        ctx.arc(frontWheelX, wheelY, wheelR * 1.28, Math.PI, 0, false);
        ctx.rect(rearWheelX, wheelY - wheelR * 1.28, frontWheelX - rearWheelX, wheelR * 1.28);
        ctx.fill();

        const bodyGrad = ctx.createLinearGradient(carX, bodyY, carX, bodyY + carHeight);
        bodyGrad.addColorStop(0, '#e95b54');
        bodyGrad.addColorStop(0.45, '#b7282d');
        bodyGrad.addColorStop(0.75, '#83171c');
        bodyGrad.addColorStop(1, '#5e1114');
        ctx.fillStyle = bodyGrad;
        roundRectPath(carX + carWidth * 0.03, bodyY + carHeight * 0.16, carWidth * 0.94, carHeight * 0.56, carHeight * 0.18);
        ctx.fill();

        ctx.beginPath();
        ctx.moveTo(carX + carWidth * 0.22, bodyY + carHeight * 0.22);
        ctx.lineTo(carX + carWidth * 0.35, bodyY - carHeight * 0.12);
        ctx.lineTo(carX + carWidth * 0.64, bodyY - carHeight * 0.14);
        ctx.lineTo(carX + carWidth * 0.82, bodyY + carHeight * 0.2);
        ctx.closePath();
        ctx.fill();

        ctx.fillStyle = 'rgba(255,255,255,0.14)';
        roundRectPath(carX + carWidth * 0.08, bodyY + carHeight * 0.18, carWidth * 0.78, carHeight * 0.08, carHeight * 0.05);
        ctx.fill();

        const winGrad = ctx.createLinearGradient(carX, bodyY - carHeight * 0.12, carX, bodyY + carHeight * 0.35);
        winGrad.addColorStop(0, 'rgba(219, 240, 255, 0.95)');
        winGrad.addColorStop(0.55, 'rgba(105, 146, 178, 0.88)');
        winGrad.addColorStop(1, 'rgba(39, 60, 80, 0.9)');
        ctx.fillStyle = winGrad;

        ctx.beginPath();
        ctx.moveTo(carX + carWidth * 0.28, bodyY + carHeight * 0.18);
        ctx.lineTo(carX + carWidth * 0.37, bodyY - carHeight * 0.06);
        ctx.lineTo(carX + carWidth * 0.51, bodyY - carHeight * 0.08);
        ctx.lineTo(carX + carWidth * 0.51, bodyY + carHeight * 0.18);
        ctx.closePath();
        ctx.fill();

        ctx.beginPath();
        ctx.moveTo(carX + carWidth * 0.54, bodyY + carHeight * 0.18);
        ctx.lineTo(carX + carWidth * 0.56, bodyY - carHeight * 0.08);
        ctx.lineTo(carX + carWidth * 0.72, bodyY - carHeight * 0.02);
        ctx.lineTo(carX + carWidth * 0.78, bodyY + carHeight * 0.18);
        ctx.closePath();
        ctx.fill();

        ctx.strokeStyle = 'rgba(28, 16, 18, 0.65)';
        ctx.lineWidth = 3;
        ctx.beginPath();
        ctx.moveTo(carX + carWidth * 0.53, bodyY - carHeight * 0.08);
        ctx.lineTo(carX + carWidth * 0.53, bodyY + carHeight * 0.23);
        ctx.stroke();

        ctx.strokeStyle = 'rgba(0,0,0,0.22)';
        ctx.lineWidth = 2;
        ctx.beginPath();
        ctx.moveTo(carX + carWidth * 0.48, bodyY + carHeight * 0.2);
        ctx.lineTo(carX + carWidth * 0.48, bodyY + carHeight * 0.65);
        ctx.stroke();

        ctx.fillStyle = '#c9ced6';
        roundRectPath(carX + carWidth * 0.19, bodyY + carHeight * 0.38, carWidth * 0.04, carHeight * 0.08, 3);
        ctx.fill();

        const headlight = ctx.createRadialGradient(carX + carWidth * 0.96, bodyY + carHeight * 0.46, 0, carX + carWidth * 0.96, bodyY + carHeight * 0.46, carWidth * 0.22);
        headlight.addColorStop(0, 'rgba(255, 245, 201, 0.95)');
        headlight.addColorStop(0.28, 'rgba(255, 223, 154, 0.45)');
        headlight.addColorStop(1, 'rgba(255, 223, 154, 0)');
        ctx.fillStyle = headlight;
        ctx.beginPath();
        ctx.ellipse(carX + carWidth * 1.02, bodyY + carHeight * 0.48, carWidth * 0.22, carHeight * 0.23, -0.1, 0, Math.PI * 2);
        ctx.fill();

        ctx.fillStyle = '#ffe6b0';
        roundRectPath(carX + carWidth * 0.93, bodyY + carHeight * 0.4, carWidth * 0.04, carHeight * 0.12, 2);
        ctx.fill();
        ctx.fillStyle = '#ffb2a3';
        roundRectPath(carX + carWidth * 0.05, bodyY + carHeight * 0.42, carWidth * 0.025, carHeight * 0.11, 2);
        ctx.fill();

        ctx.fillStyle = 'rgba(255,255,255,0.12)';
        ctx.beginPath();
        ctx.moveTo(carX + carWidth * 0.17, bodyY + carHeight * 0.28);
        ctx.quadraticCurveTo(carX + carWidth * 0.45, bodyY + carHeight * 0.12, carX + carWidth * 0.84, bodyY + carHeight * 0.25);
        ctx.lineTo(carX + carWidth * 0.81, bodyY + carHeight * 0.3);
        ctx.quadraticCurveTo(carX + carWidth * 0.48, bodyY + carHeight * 0.18, carX + carWidth * 0.2, bodyY + carHeight * 0.34);
        ctx.closePath();
        ctx.fill();

        drawWheel(rearWheelX, wheelY, wheelR, rotation);
        drawWheel(frontWheelX, wheelY, wheelR, rotation);

        ctx.restore();
      }

      function drawForegroundBlur(time) {
        const spacing = 90;
        const offset = mod(time * 320, spacing);
        for (let i = -3; i < Math.ceil(width / spacing) + 6; i++) {
          const idx = i + 5000;
          const x = i * spacing - offset;
          const h = 14 + hash(idx) * 22;
          ctx.fillStyle = `rgba(${45 + Math.round(hash(idx + 1) * 30)}, ${55 + Math.round(hash(idx + 2) * 40)}, ${25 + Math.round(hash(idx + 3) * 20)}, 0.85)`;
          ctx.beginPath();
          ctx.ellipse(x, roadTop + 6, 22 + hash(idx + 4) * 12, h, 0, 0, Math.PI * 2);
          ctx.fill();
        }
      }

      function render(now) {
        const time = (now - state.start) / 1000;
        ctx.clearRect(0, 0, width, height);
        drawSky(time);
        drawGroundLayers(time);
        drawRoad(time);
        drawCar(time);
        drawForegroundBlur(time);
        requestAnimationFrame(render);
      }

      window.addEventListener('resize', resize);
      resize();
      requestAnimationFrame(render);
    })();
  </script>
</body>
</html>

Claude Opus 4.7

👑 Premium
40.8
TPS
1.8m
Duration
17.3KB
Output
4436
Tokens
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Sunset Drive</title>
<style>
  html,body{margin:0;padding:0;height:100%;background:#000;overflow:hidden;font-family:sans-serif;}
  canvas{display:block;width:100vw;height:100vh;}
</style>
</head>
<body>
<canvas id="c"></canvas>
<script>
(() => {
  const cv = document.getElementById('c');
  const ctx = cv.getContext('2d');
  let W=0,H=0,DPR=Math.max(1,window.devicePixelRatio||1);

  function resize(){
    W = cv.clientWidth = window.innerWidth;
    H = cv.clientHeight = window.innerHeight;
    cv.width = W*DPR; cv.height = H*DPR;
    ctx.setTransform(DPR,0,0,DPR,0,0);
    buildSky();
  }
  window.addEventListener('resize',resize);

  // ---------- Sky gradient (sunset/dusk) ----------
  let skyGrad=null, sunGlow=null;
  function buildSky(){
    skyGrad = ctx.createLinearGradient(0,0,0,H);
    skyGrad.addColorStop(0.00,'#1a1340');
    skyGrad.addColorStop(0.25,'#3b1f6b');
    skyGrad.addColorStop(0.50,'#a13a6b');
    skyGrad.addColorStop(0.72,'#f08a4b');
    skyGrad.addColorStop(0.85,'#fbc56a');
    skyGrad.addColorStop(1.00,'#fde2a2');
    sunGlow = ctx.createRadialGradient(W*0.72,H*0.62,5,W*0.72,H*0.62,Math.max(W,H)*0.55);
    sunGlow.addColorStop(0,'rgba(255,220,150,0.9)');
    sunGlow.addColorStop(0.15,'rgba(255,170,100,0.45)');
    sunGlow.addColorStop(0.45,'rgba(255,120,80,0.15)');
    sunGlow.addColorStop(1,'rgba(255,120,80,0)');
  }

  // ---------- Deterministic pseudo-random ----------
  function rand(seed){
    let s = seed*9301+49297;
    return function(){
      s = (s*9301+49297)%233280;
      return s/233280;
    };
  }

  // ---------- Layers ----------
  // Each layer: speed multiplier, draw(offset) function with seamless tiling
  let stars = [];
  function buildStars(){
    stars = [];
    const r = rand(7);
    for(let i=0;i<140;i++){
      stars.push({x:r()*1, y:r()*0.45, s:r()*1.4+0.2, a:r()*0.6+0.2});
    }
  }
  buildStars();

  function drawStars(){
    for(const s of stars){
      const y = s.y*H;
      // fade stars near sun horizon area
      const alpha = s.a * Math.max(0, 1 - y/(H*0.55));
      if(alpha<=0) continue;
      ctx.fillStyle = `rgba(255,240,210,${alpha})`;
      ctx.fillRect(s.x*W, y, s.s, s.s);
    }
  }

  // Mountains (far) - drawn as tiled silhouettes
  function makeMountainPath(seed, segWidth, amp, baseY){
    const r = rand(seed);
    const pts=[];
    let x=0;
    while(x < segWidth+200){
      const step = 60+r()*120;
      pts.push({x, y: baseY - (r()*amp + amp*0.2)});
      x += step;
    }
    return pts;
  }

  const farMountains = makeMountainPath(11, 2400, 180, 0);
  const midMountains = makeMountainPath(23, 2000, 120, 0);
  const hills        = makeMountainPath(37, 1600, 70, 0);

  function drawMountainLayer(pts, segWidth, offset, baseY, color, glow){
    // tile across screen
    const totalW = segWidth;
    let startX = -((offset % totalW) + totalW) % totalW;
    ctx.fillStyle = color;
    for(let tile=0; tile<3; tile++){
      const ox = startX + tile*totalW;
      ctx.beginPath();
      ctx.moveTo(ox, baseY);
      for(const p of pts){
        ctx.lineTo(ox + p.x, baseY + p.y);
      }
      ctx.lineTo(ox + segWidth + 200, baseY);
      ctx.lineTo(ox + segWidth + 200, H);
      ctx.lineTo(ox, H);
      ctx.closePath();
      ctx.fill();
    }
    if(glow){
      // rim light on top edge facing sun
      ctx.save();
      ctx.globalCompositeOperation='lighter';
      ctx.strokeStyle = glow;
      ctx.lineWidth = 1.2;
      for(let tile=0; tile<3; tile++){
        const ox = startX + tile*totalW;
        ctx.beginPath();
        ctx.moveTo(ox, baseY);
        for(const p of pts){
          ctx.lineTo(ox + p.x, baseY + p.y);
        }
        ctx.lineTo(ox + segWidth + 200, baseY);
        ctx.stroke();
      }
      ctx.restore();
    }
  }

  // Trees
  function drawTree(x, groundY, scale, color){
    ctx.fillStyle = color;
    // trunk
    ctx.fillRect(x-2*scale, groundY-22*scale, 4*scale, 22*scale);
    // foliage: layered triangles
    ctx.beginPath();
    ctx.moveTo(x, groundY-60*scale);
    ctx.lineTo(x-18*scale, groundY-25*scale);
    ctx.lineTo(x+18*scale, groundY-25*scale);
    ctx.closePath();
    ctx.fill();
    ctx.beginPath();
    ctx.moveTo(x, groundY-48*scale);
    ctx.lineTo(x-22*scale, groundY-15*scale);
    ctx.lineTo(x+22*scale, groundY-15*scale);
    ctx.closePath();
    ctx.fill();
  }

  function drawBush(x, groundY, scale, color){
    ctx.fillStyle = color;
    ctx.beginPath();
    ctx.ellipse(x, groundY-6*scale, 16*scale, 8*scale, 0, 0, Math.PI*2);
    ctx.fill();
    ctx.beginPath();
    ctx.ellipse(x-10*scale, groundY-4*scale, 10*scale, 6*scale, 0, 0, Math.PI*2);
    ctx.fill();
    ctx.beginPath();
    ctx.ellipse(x+10*scale, groundY-4*scale, 10*scale, 6*scale, 0, 0, Math.PI*2);
    ctx.fill();
  }

  // Pre-generate roadside element positions per tile
  function genRoadsideTile(seed, tileWidth){
    const r = rand(seed);
    const items=[];
    let x=20;
    while(x < tileWidth-20){
      const t = r();
      let kind='tree';
      if(t<0.55) kind='tree';
      else if(t<0.8) kind='bush';
      else if(t<0.92) kind='pole';
      else kind='sign';
      items.push({x, kind, scale: 0.9+r()*0.6, jitterY: (r()-0.5)*4});
      x += 50 + r()*70;
    }
    return items;
  }

  const farTreeTile  = genRoadsideTile(101, 1800);
  const midTreeTile  = genRoadsideTile(202, 1400);
  const nearTile     = genRoadsideTile(303, 900);

  function drawPole(x, groundY, scale){
    ctx.fillStyle = '#1a1320';
    ctx.fillRect(x-1.5*scale, groundY-70*scale, 3*scale, 70*scale);
    // crossbar
    ctx.fillRect(x-12*scale, groundY-66*scale, 24*scale, 2*scale);
    // wires sag (simple)
    ctx.strokeStyle = 'rgba(20,15,25,0.7)';
    ctx.lineWidth = 1;
    ctx.beginPath();
    ctx.moveTo(x-12*scale, groundY-64*scale);
    ctx.quadraticCurveTo(x, groundY-58*scale, x+12*scale, groundY-64*scale);
    ctx.stroke();
  }

  function drawSign(x, groundY, scale){
    ctx.fillStyle = '#221820';
    ctx.fillRect(x-1*scale, groundY-30*scale, 2*scale, 30*scale);
    ctx.fillStyle = '#e8d28a';
    ctx.fillRect(x-10*scale, groundY-40*scale, 20*scale, 12*scale);
    ctx.fillStyle = '#3a2a18';
    ctx.fillRect(x-7*scale, groundY-36*scale, 14*scale, 2*scale);
    ctx.fillRect(x-7*scale, groundY-32*scale, 10*scale, 2*scale);
  }

  function drawRoadsideTile(items, ox, groundY, treeColor, depth){
    for(const it of items){
      const x = ox + it.x;
      const gy = groundY + it.jitterY;
      if(it.kind==='tree') drawTree(x, gy, it.scale*depth, treeColor);
      else if(it.kind==='bush') drawBush(x, gy, it.scale*depth, treeColor);
      else if(it.kind==='pole') drawPole(x, gy, it.scale*depth);
      else if(it.kind==='sign') drawSign(x, gy, it.scale*depth);
    }
  }

  function drawTiledRoadside(items, tileWidth, offset, groundY, color, depth){
    let startX = -((offset % tileWidth) + tileWidth) % tileWidth;
    for(let tile=0; tile<3; tile++){
      drawRoadsideTile(items, startX + tile*tileWidth, groundY, color, depth);
    }
  }

  // Ground stripes / road dashes
  function drawRoad(offset, roadTop, roadBottom){
    // road body
    const grd = ctx.createLinearGradient(0, roadTop, 0, roadBottom);
    grd.addColorStop(0,'#2a2530');
    grd.addColorStop(1,'#13101a');
    ctx.fillStyle = grd;
    ctx.fillRect(0, roadTop, W, roadBottom-roadTop);

    // edge highlight (sunset reflection on asphalt)
    const edge = ctx.createLinearGradient(0, roadTop, 0, roadTop+8);
    edge.addColorStop(0,'rgba(255,170,100,0.35)');
    edge.addColorStop(1,'rgba(255,170,100,0)');
    ctx.fillStyle = edge;
    ctx.fillRect(0, roadTop, W, 8);

    // center dashed line
    const dashLen = 40, gap = 30;
    const period = dashLen+gap;
    let startX = -((offset % period) + period) % period;
    const lineY = (roadTop+roadBottom)/2 - 2;
    ctx.fillStyle = 'rgba(255,220,150,0.85)';
    for(let x=startX; x<W+period; x+=period){
      ctx.fillRect(x, lineY, dashLen, 4);
    }

    // side curbs
    ctx.fillStyle = 'rgba(255,255,255,0.06)';
    ctx.fillRect(0, roadTop+2, W, 1);
    ctx.fillStyle = 'rgba(0,0,0,0.4)';
    ctx.fillRect(0, roadBottom-2, W, 2);
  }

  // Ground (foreground grass / dirt)
  function drawGround(offset, groundY, roadTop){
    // band between groundY (top of grass) and roadTop
    const grd = ctx.createLinearGradient(0, groundY-10, 0, roadTop);
    grd.addColorStop(0,'#3b2a3a');
    grd.addColorStop(1,'#1f1722');
    ctx.fillStyle = grd;
    ctx.fillRect(0, groundY-10, W, roadTop-(groundY-10));

    // moving grass tufts on near ground
    const period = 18;
    let startX = -((offset % period) + period) % period;
    ctx.fillStyle = 'rgba(60,40,55,0.9)';
    for(let x=startX; x<W+period; x+=period){
      ctx.fillRect(x, groundY-3, 4, 3);
      ctx.fillRect(x+8, groundY-2, 3, 2);
    }
  }

  // ---------- Car ----------
  function drawCar(cx, cy, bob, wheelAngle){
    ctx.save();
    ctx.translate(cx, cy + bob);

    const scale = Math.min(W,H)/620;
    ctx.scale(scale, scale);

    // shadow
    ctx.fillStyle = 'rgba(0,0,0,0.45)';
    ctx.beginPath();
    ctx.ellipse(0, 70, 150, 14, 0, 0, Math.PI*2);
    ctx.fill();

    // body main shape
    // lower body
    ctx.fillStyle = '#b53a2a';
    roundedBody();

    // window/cabin
    ctx.fillStyle = '#0e1a2a';
    ctx.beginPath();
    ctx.moveTo(-58, 0);
    ctx.lineTo(-30, -36);
    ctx.lineTo(40, -36);
    ctx.lineTo(70, 0);
    ctx.closePath();
    ctx.fill();

    // window reflections (sunset)
    const wr = ctx.createLinearGradient(-58,-36,70,0);
    wr.addColorStop(0,'rgba(255,180,120,0.4)');
    wr.addColorStop(0.5,'rgba(255,210,150,0.15)');
    wr.addColorStop(1,'rgba(255,180,120,0.35)');
    ctx.fillStyle = wr;
    ctx.beginPath();
    ctx.moveTo(-58, 0);
    ctx.lineTo(-30, -36);
    ctx.lineTo(40, -36);
    ctx.lineTo(70, 0);
    ctx.closePath();
    ctx.fill();

    // window divider (B-pillar)
    ctx.fillStyle = '#7a2418';
    ctx.fillRect(2, -36, 3, 36);

    // body trim line
    ctx.strokeStyle = 'rgba(0,0,0,0.35)';
    ctx.lineWidth = 1.5;
    ctx.beginPath();
    ctx.moveTo(-110, 12);
    ctx.lineTo(120, 12);
    ctx.stroke();

    // headlight glow
    ctx.save();
    ctx.globalCompositeOperation='lighter';
    const hl = ctx.createRadialGradient(122, 8, 1, 122, 8, 60);
    hl.addColorStop(0,'rgba(255,240,200,0.9)');
    hl.addColorStop(1,'rgba(255,240,200,0)');
    ctx.fillStyle = hl;
    ctx.fillRect(80, -30, 120, 80);
    ctx.restore();
    // headlight bulb
    ctx.fillStyle = '#fff5d6';
    ctx.beginPath();
    ctx.ellipse(120, 8, 5, 4, 0, 0, Math.PI*2);
    ctx.fill();

    // taillight
    ctx.fillStyle = '#ff3a2a';
    ctx.fillRect(-122, 4, 5, 8);
    ctx.save();
    ctx.globalCompositeOperation='lighter';
    const tl = ctx.createRadialGradient(-120, 8, 1, -120, 8, 25);
    tl.addColorStop(0,'rgba(255,60,40,0.7)');
    tl.addColorStop(1,'rgba(255,60,40,0)');
    ctx.fillStyle = tl;
    ctx.fillRect(-150, -10, 50, 40);
    ctx.restore();

    // door handle
    ctx.fillStyle = 'rgba(0,0,0,0.5)';
    ctx.fillRect(-30, -4, 14, 3);
    ctx.fillRect(20, -4, 14, 3);

    // wheels
    drawWheel(-70, 50, wheelAngle);
    drawWheel(70, 50, wheelAngle);

    // wheel arches (over wheels for depth)
    ctx.fillStyle = '#3a1a14';
    ctx.beginPath();
    ctx.arc(-70, 50, 28, Math.PI, 0, false);
    ctx.lineTo(-42, 50);
    ctx.arc(-70, 50, 22, 0, Math.PI, true);
    ctx.closePath();
    ctx.fill();
    ctx.beginPath();
    ctx.arc(70, 50, 28, Math.PI, 0, false);
    ctx.lineTo(98, 50);
    ctx.arc(70, 50, 22, 0, Math.PI, true);
    ctx.closePath();
    ctx.fill();

    ctx.restore();

    function roundedBody(){
      ctx.beginPath();
      ctx.moveTo(-130, 20);
      ctx.quadraticCurveTo(-135, 0, -110, -4);
      ctx.lineTo(-70, -6);
      ctx.quadraticCurveTo(-60, -8, -52, -14);
      ctx.lineTo(-30, -36);
      ctx.quadraticCurveTo(-20, -42, -8, -42);
      ctx.lineTo(35, -42);
      ctx.quadraticCurveTo(48, -42, 56, -32);
      ctx.lineTo(72, -10);
      ctx.quadraticCurveTo(80, -4, 92, -2);
      ctx.lineTo(120, 2);
      ctx.quadraticCurveTo(135, 6, 132, 24);
      ctx.lineTo(128, 50);
      ctx.lineTo(98, 50);
      ctx.arc(70, 50, 28, 0, Math.PI, true);
      ctx.lineTo(-42, 50);
      ctx.arc(-70, 50, 28, 0, Math.PI, true);
      ctx.lineTo(-128, 50);
      ctx.closePath();
      ctx.fill();

      // body shading
      const bg = ctx.createLinearGradient(0, -42, 0, 50);
      bg.addColorStop(0,'rgba(255,255,255,0.18)');
      bg.addColorStop(0.5,'rgba(255,255,255,0)');
      bg.addColorStop(1,'rgba(0,0,0,0.35)');
      ctx.fillStyle = bg;
      ctx.fill();

      // sunset rim light on top
      const rim = ctx.createLinearGradient(0,-42,0,-20);
      rim.addColorStop(0,'rgba(255,180,120,0.55)');
      rim.addColorStop(1,'rgba(255,180,120,0)');
      ctx.fillStyle = rim;
      ctx.fill();
    }
  }

  function drawWheel(x, y, ang){
    ctx.save();
    ctx.translate(x,y);
    // tire
    ctx.fillStyle = '#0a0810';
    ctx.beginPath();
    ctx.arc(0,0,22,0,Math.PI*2);
    ctx.fill();
    // sidewall
    ctx.fillStyle = '#181420';
    ctx.beginPath();
    ctx.arc(0,0,18,0,Math.PI*2);
    ctx.fill();
    // hub
    ctx.fillStyle = '#cfcfd6';
    ctx.beginPath();
    ctx.arc(0,0,11,0,Math.PI*2);
    ctx.fill();
    // spokes
    ctx.rotate(ang);
    ctx.strokeStyle = '#2a2630';
    ctx.lineWidth = 3;
    for(let i=0;i<5;i++){
      ctx.rotate(Math.PI*2/5);
      ctx.beginPath();
      ctx.moveTo(0,0);
      ctx.lineTo(10,0);
      ctx.stroke();
    }
    // center cap
    ctx.fillStyle = '#5a5560';
    ctx.beginPath();
    ctx.arc(0,0,3,0,Math.PI*2);
    ctx.fill();
    ctx.restore();
  }

  // ---------- Animation loop ----------
  let last = performance.now();
  let dist = 0; // road distance traveled
  const baseSpeed = 260; // px/sec at the road layer

  function frame(now){
    const dt = Math.min(0.05,(now-last)/1000);
    last = now;
    dist += baseSpeed * dt;

    render(now/1000);
    requestAnimationFrame(frame);
  }

  function render(t){
    // Layout
    const horizon = H*0.58;
    const groundY = H*0.78;     // top of near grass
    const roadTop = H*0.82;
    const roadBottom = H*0.95;

    // Sky
    ctx.fillStyle = skyGrad;
    ctx.fillRect(0,0,W,H);

    // Stars
    drawStars();

    // Sun glow
    ctx.fillStyle = sunGlow;
    ctx.fillRect(0,0,W,H);

    // Sun disk
    ctx.save();
    ctx.globalCompositeOperation='lighter';
    const sx = W*0.72, sy = H*0.62;
    const sun = ctx.createRadialGradient(sx,sy,2,sx,sy,80);
    sun.addColorStop(0,'rgba(255,250,220,1)');
    sun.addColorStop(0.4,'rgba(255,210,150,0.9)');
    sun.addColorStop(1,'rgba(255,160,100,0)');
    ctx.fillStyle = sun;
    ctx.beginPath();
    ctx.arc(sx,sy,80,0,Math.PI*2);
    ctx.fill();
    ctx.restore();

    // Distant clouds (slow)
    drawClouds(dist*0.04, horizon);

    // Far mountains (very slow)
    drawMountainLayer(farMountains, 2400, dist*0.08, horizon-10,
      'rgba(40,25,55,0.85)', 'rgba(255,170,110,0.35)');

    // Mid mountains
    drawMountainLayer(midMountains, 2000, dist*0.18, horizon+30,
      'rgba(28,18,40,0.92)', 'rgba(255,150,100,0.3)');

    // Hills
    drawMountainLayer(hills, 1600, dist*0.32, horizon+80,
      '#150f20', null);

    // Far roadside (small trees behind hills band)
    drawTiledRoadside(farTreeTile, 1800, dist*0.45, horizon+95, '#0f0a18', 0.55);

    // Mid roadside trees
    drawTiledRoadside(midTreeTile, 1400, dist*0.7, groundY-18, '#120c1c', 0.85);

    // Ground band
    drawGround(dist*1.0, groundY, roadTop);

    // Near roadside (poles, signs, bushes) - fastest
    drawTiledRoadside(nearTile, 900, dist*1.0, groundY+2, '#0a0612', 1.05);

    // Road
    drawRoad(dist*1.2, roadTop, roadBottom);

    // Car
    const carX = W*0.42;
    const carY = roadTop - 6; // wheels sit on road
    const bob = Math.sin(t*6.2)*0.8 + Math.sin(t*2.1)*0.4;
    // wheel angular speed proportional to ground speed
    // wheel radius ~22 in car-local; effective on screen depends on scale, but we just animate
    const wheelAng = (dist*0.06) % (Math.PI*2);
    drawCar(carX, carY-50, bob, wheelAng);

    // foreground vignette
    const vg = ctx.createRadialGradient(W/2, H/2, Math.min(W,H)*0.35, W/2, H/2, Math.max(W,H)*0.75);
    vg.addColorStop(0,'rgba(0,0,0,0)');
    vg.addColorStop(1,'rgba(0,0,0,0.55)');
    ctx.fillStyle = vg;
    ctx.fillRect(0,0,W,H);

    // subtle motion blur lines on road edges
    ctx.save();
    ctx.globalAlpha = 0.15;
    ctx.fillStyle = '#000';
    for(let i=0;i<6;i++){
      const y = roadBottom - i*2;
      ctx.fillRect(0, y, W, 1);
    }
    ctx.restore();
  }

  // Clouds
  const cloudSeed = rand(55);
  const clouds = [];
  for(let i=0;i<8;i++){
    clouds.push({
      x: cloudSeed()*1800,
      y: cloudSeed()*0.25 + 0.1,
      w: 80+cloudSeed()*140,
      h: 12+cloudSeed()*10,
      a: 0.25+cloudSeed()*0.35
    });
  }
  function drawClouds(offset, horizon){
    const tileW = 1800;
    let startX = -((offset % tileW) + tileW) % tileW;
    for(let tile=0; tile<3; tile++){
      const ox = startX + tile*tileW;
      for(const c of clouds){
        const x = ox + c.x;
        const y = c.y * H;
        ctx.fillStyle = `rgba(255,200,160,${c.a})`;
        ctx.beginPath();
        ctx.ellipse(x, y, c.w*0.5, c.h, 0, 0, Math.PI*2);
        ctx.fill();
        ctx.beginPath();
        ctx.ellipse(x+c.w*0.2, y+2, c.w*0.35, c.h*0.8, 0, 0, Math.PI*2);
        ctx.fill();
        ctx.beginPath();
        ctx.ellipse(x-c.w*0.25, y+2, c.w*0.3, c.h*0.7, 0, 0, Math.PI*2);
        ctx.fill();
      }
    }
  }

  resize();
  requestAnimationFrame(frame);
})();
</script>
</body>
</html>