Alpine Ski Racing

A fun game created with AI

🤖 System_default ⚡ 139.2s 🎯 21,107 tokens $0.2168

Created by FunGamer3686 • November 25, 2025

View Game Code
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
    <title>Alpine Ski Racing</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            -webkit-tap-highlight-color: transparent;
        }
        body {
            margin: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            background: linear-gradient(180deg, #87CEEB 0%, #E0F6FF 100%);
            font-family: 'Arial', sans-serif;
            overflow: hidden;
            touch-action: manipulation;
            padding-top: env(safe-area-inset-top);
            padding-bottom: env(safe-area-inset-bottom);
            padding-left: env(safe-area-inset-left);
            padding-right: env(safe-area-inset-right);
            -webkit-user-select: none;
            user-select: none;
            -webkit-touch-callout: none;
        }
        #gameContainer {
            position: relative;
            width: 100%;
            height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
        }
        canvas {
            border: 3px solid #2C3E50;
            background: linear-gradient(180deg, #E8F4F8 0%, #FFFFFF 100%);
            box-shadow: 0 10px 30px rgba(0,0,0,0.3);
            touch-action: none;
            max-width: 100vw;
            max-height: 100vh;
            display: block;
        }
        #ui {
            position: absolute;
            top: max(10px, env(safe-area-inset-top, 10px) + 10px);
            left: max(10px, env(safe-area-inset-left, 10px) + 10px);
            right: max(10px, env(safe-area-inset-right, 10px) + 10px);
            color: white;
            font-weight: bold;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
            pointer-events: none;
            z-index: 10;
        }
        .stat {
            background: rgba(0,0,0,0.6);
            padding: clamp(8px, 2vw, 12px);
            border-radius: 8px;
            margin-bottom: 8px;
            font-size: clamp(14px, 3vw, 18px);
        }
        #controls {
            position: absolute;
            bottom: max(20px, env(safe-area-inset-bottom, 20px) + 20px);
            left: 50%;
            transform: translateX(-50%);
            color: white;
            text-align: center;
            font-size: clamp(12px, 2.5vw, 14px);
            text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
            background: rgba(0,0,0,0.5);
            padding: clamp(8px, 2vw, 12px);
            border-radius: 8px;
        }
        .particle {
            position: absolute;
            pointer-events: none;
        }
    </style>
</head>
<body>
    <div id="gameContainer">
        <canvas id="game"></canvas>
        <div id="ui">
            <div class="stat" id="score">Score: 0</div>
            <div class="stat" id="speed">Speed: 0 km/h</div>
            <div class="stat" id="gates">Gates: 0/0</div>
            <div class="stat" id="coins">💰 Coins: 0</div>
            <div class="stat" id="gems">💎 Gems: 0</div>
            <div class="stat" id="level">Level: 1</div>
            <div class="stat" id="slope">Slope: Beginner</div>
        </div>
        <div id="controls">⬅️➡️ Swipe to Turn | Space/Tap Brake | W/↑ Double Jump</div>
    </div>
    <script>
        window.addEventListener('load', function() {
            try {
                const canvas = document.getElementById('game');
                if (!canvas) { alert('Canvas not found'); return; }
                const ctx = canvas.getContext('2d');
                if (!ctx) { alert('Canvas context failed'); return; }

                // Responsive canvas setup
                function resizeCanvas() {
                    const maxWidth = Math.min(window.innerWidth * 0.95, 500);
                    const maxHeight = Math.min(window.innerHeight * 0.85, 700);
                    canvas.style.width = maxWidth + 'px';
                    canvas.style.height = maxHeight + 'px';
                    const dpr = window.devicePixelRatio || 1;
                    canvas.width = maxWidth * dpr;
                    canvas.height = maxHeight * dpr;
                    ctx.scale(dpr, dpr);
                }
                resizeCanvas();
                window.addEventListener('resize', resizeCanvas);
                window.addEventListener('orientationchange', () => setTimeout(resizeCanvas, 100));

                // Game constants
                const GAME_WIDTH = parseInt(canvas.style.width);
                const GAME_HEIGHT = parseInt(canvas.style.height);

                // Keyboard controls
                const keys = {};
                window.addEventListener('keydown', (e) => {
                    if (!keys[e.key]) {
                        keys[e.key] = true;
                        if(['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', ' ', 'w', 'W'].includes(e.key)) {
                            e.preventDefault();
                        }
                    }
                });
                window.addEventListener('keyup', (e) => {
                    keys[e.key] = false;
                });

                // Touch controls
                let touchStartX = 0;
                let touchStartY = 0;
                let isTouching = false;
                let touchJumpUsed = false;
                
                canvas.addEventListener('touchstart', (e) => {
                    e.preventDefault();
                    touchStartX = e.touches[0].clientX;
                    touchStartY = e.touches[0].clientY;
                    isTouching = true;
                    touchJumpUsed = false;
                }, { passive: false });

                canvas.addEventListener('touchmove', (e) => {
                    e.preventDefault();
                    if (!isTouching) return;
                    const deltaX = e.touches[0].clientX - touchStartX;
                    const deltaY = touchStartY - e.touches[0].clientY;
                    
                    // Horizontal swipe for turning
                    if (Math.abs(deltaX) > 10) {
                        if (deltaX > 0) {
                            keys['ArrowRight'] = true;
                            keys['ArrowLeft'] = false;
                        } else {
                            keys['ArrowLeft'] = true;
                            keys['ArrowRight'] = false;
                        }
                    }
                    
                    // Upward swipe for jump
                    if (deltaY > 30 && !touchJumpUsed) {
                        keys['ArrowUp'] = true;
                        touchJumpUsed = true;
                        setTimeout(() => keys['ArrowUp'] = false, 100);
                    }
                }, { passive: false });

                canvas.addEventListener('touchend', (e) => {
                    e.preventDefault();
                    isTouching = false;
                    keys['ArrowLeft'] = false;
                    keys['ArrowRight'] = false;
                    touchJumpUsed = false;
                }, { passive: false });

                // Tap to brake
                let lastTapTime = 0;
                canvas.addEventListener('click', (e) => {
                    const now = Date.now();
                    if (now - lastTapTime < 300) return; // Prevent double-tap issues
                    lastTapTime = now;
                    keys[' '] = true;
                    setTimeout(() => keys[' '] = false, 100);
                });

                // Game state
                let gameState = {
                    score: 0,
                    speed: 0,
                    gatesPassed: 0,
                    totalGates: 15,
                    coins: 0,
                    gems: 0,
                    level: 1,
                    slopeLevel: 0,
                    slopeNames: ['Beginner', 'Intermediate', 'Advanced', 'Expert'],
                    gameOver: false,
                    raceComplete: false,
                    invincible: false,
                    invincibleTimer: 0,
                    speedBoost: false,
                    speedBoostTimer: 0
                };

                // Player (skier)
                const player = {
                    x: GAME_WIDTH / 2,
                    y: GAME_HEIGHT * 0.75,
                    width: 20,
                    height: 30,
                    vx: 0,
                    baseSpeed: 3,
                    maxSpeed: 8,
                    turnSpeed: 0.3,
                    carving: false,
                    jumpHeight: 0,
                    isJumping: false,
                    jumpVelocity: 0,
                    jumpsAvailable: 2,
                    maxJumps: 2,
                    onGround: true
                };

                // Slope elements
                let gates = [];
                let obstacles = [];
                let jumps = [];
                let snowflakes = [];
                let trees = [];
                let enemies = [];
                let coins = [];
                let gems = [];
                let powerUps = [];
                let particles = [];
                let scrollSpeed = 3;

                // Weather
                let weather = 'clear';

                // Helper functions
                function createParticle(x, y, color, count = 5) {
                    for (let i = 0; i < count; i++) {
                        particles.push({
                            x: x,
                            y: y,
                            vx: (Math.random() - 0.5) * 4,
                            vy: (Math.random() - 0.5) * 4 - 2,
                            life: 30,
                            color: color,
                            size: 3 + Math.random() * 3
                        });
                    }
                }

                function checkCollision(obj1, obj2, radius1, radius2) {
                    const dx = obj1.x - obj2.x;
                    const dy = obj1.y - obj2.y;
                    const dist = Math.sqrt(dx * dx + dy * dy);
                    return dist < radius1 + radius2;
                }

                // Initialize level
                function initLevel() {
                    gates = [];
                    obstacles = [];
                    jumps = [];
                    trees = [];
                    enemies = [];
                    coins = [];
                    gems = [];
                    powerUps = [];
                    particles = [];
                    gameState.gatesPassed = 0;
                    gameState.raceComplete = false;
                    gameState.gameOver = false;
                    player.x = GAME_WIDTH / 2;
                    player.vx = 0;
                    player.jumpHeight = 0;
                    player.isJumping = false;
                    player.jumpsAvailable = player.maxJumps;
                    player.onGround = true;

                    const levelLength = gameState.totalGates * 200;

                    // Create gates
                    for (let i = 0; i < gameState.totalGates; i++) {
                        const y = -100 - i * 200;
                        const gateWidth = 120 - gameState.slopeLevel * 15;
                        const centerX = GAME_WIDTH / 2 + (Math.random() - 0.5) * 100;
                        gates.push({
                            x: centerX,
                            y: y,
                            width: gateWidth,
                            passed: false,
                            side: i % 2 === 0 ? 'left' : 'right'
                        });
                    }

                    // Create coins (frequent)
                    const coinCount = 30 + gameState.level * 5;
                    for (let i = 0; i < coinCount; i++) {
                        coins.push({
                            x: 50 + Math.random() * (GAME_WIDTH - 100),
                            y: -200 - Math.random() * levelLength,
                            radius: 10,
                            collected: false,
                            rotation: Math.random() * Math.PI * 2
                        });
                    }

                    // Create gems (rare, higher value)
                    const gemCount = 5 + gameState.level * 2;
                    for (let i = 0; i < gemCount; i++) {
                        gems.push({
                            x: 50 + Math.random() * (GAME_WIDTH - 100),
                            y: -200 - Math.random() * levelLength,
                            radius: 12,
                            collected: false,
                            sparkle: Math.random() * Math.PI * 2
                        });
                    }

                    // Create power-ups
                    const powerUpCount = 3 + gameState.level;
                    for (let i = 0; i < powerUpCount; i++) {
                        const types = ['invincible', 'speedBoost'];
                        powerUps.push({
                            x: 50 + Math.random() * (GAME_WIDTH - 100),
                            y: -200 - Math.random() * levelLength,
                            radius: 15,
                            collected: false,
                            type: types[Math.floor(Math.random() * types.length)],
                            pulse: 0
                        });
                    }

                    // Create jumps
                    for (let i = 0; i < 3 + gameState.slopeLevel; i++) {
                        jumps.push({
                            x: GAME_WIDTH / 2 + (Math.random() - 0.5) * 100,
                            y: -200 - Math.random() * levelLength,
                            width: 80,
                            height: 15
                        });
                    }

                    // Create obstacles (rocks)
                    for (let i = 0; i < 5 + gameState.slopeLevel * 2; i++) {
                        obstacles.push({
                            x: 30 + Math.random() * (GAME_WIDTH - 60),
                            y: -200 - Math.random() * levelLength,
                            radius: 15 + Math.random() * 10
                        });
                    }

                    // Create enemies (snowboarders)
                    const enemyCount = 3 + gameState.slopeLevel * 2 + Math.floor(gameState.level / 2);
                    for (let i = 0; i < enemyCount; i++) {
                        enemies.push({
                            x: 50 + Math.random() * (GAME_WIDTH - 100),
                            y: -300 - Math.random() * levelLength,
                            vx: (Math.random() - 0.5) * 2,
                            width: 20,
                            height: 25,
                            zigzag: Math.random() > 0.5,
                            zigzagTimer: 0,
                            color: ['#FF1493', '#00CED1', '#FFD700', '#9370DB'][Math.floor(Math.random() * 4)]
                        });
                    }

                    // Create trees
                    for (let i = 0; i < 20; i++) {
                        const side = Math.random() > 0.5 ? 'left' : 'right';
                        trees.push({
                            x: side === 'left' ? Math.random() * 40 : GAME_WIDTH - Math.random() * 40,
                            y: -Math.random() * levelLength,
                            size: 20 + Math.random() * 15
                        });
                    }

                    // Weather
                    weather = Math.random() > 0.7 ? 'snowing' : 'clear';
                    if (weather === 'snowing') {
                        snowflakes = [];
                        for (let i = 0; i < 100; i++) {
                            snowflakes.push({
                                x: Math.random() * GAME_WIDTH,
                                y: Math.random() * GAME_HEIGHT,
                                speed: 1 + Math.random() * 2,
                                size: 2 + Math.random() * 3
                            });
                        }
                    }
                }

                initLevel();

                // Update function
                function update() {
                    if (gameState.gameOver || gameState.raceComplete) return;

                    // Update power-up timers
                    if (gameState.invincible) {
                        gameState.invincibleTimer--;
                        if (gameState.invincibleTimer <= 0) {
                            gameState.invincible = false;
                        }
                    }
                    if (gameState.speedBoost) {
                        gameState.speedBoostTimer--;
                        if (gameState.speedBoostTimer <= 0) {
                            gameState.speedBoost = false;
                        }
                    }

                    // Player movement
                    if (keys['ArrowLeft'] || keys['a'] || keys['A']) {
                        player.vx -= player.turnSpeed;
                        player.carving = true;
                    } else if (keys['ArrowRight'] || keys['d'] || keys['D']) {
                        player.vx += player.turnSpeed;
                        player.carving = true;
                    } else {
                        player.carving = false;
                    }

                    // Double jump mechanic
                    if ((keys['ArrowUp'] || keys['w'] || keys['W']) && player.jumpsAvailable > 0) {
                        if (!player.isJumping || (player.jumpsAvailable === 1 && player.jumpVelocity < 0)) {
                            player.isJumping = true;
                            player.jumpVelocity = -8;
                            player.jumpsAvailable--;
                            createParticle(player.x, player.y + 15, '#FFFFFF', 8);
                            keys['ArrowUp'] = false;
                            keys['w'] = false;
                            keys['W'] = false;
                        }
                    }

                    // Braking
                    if (keys[' ']) {
                        scrollSpeed = Math.max(1, scrollSpeed - 0.2);
                    } else {
                        const baseSpeed = player.baseSpeed + gameState.slopeLevel;
                        const maxSpeed = gameState.speedBoost ? baseSpeed * 1.5 : baseSpeed;
                        scrollSpeed = Math.min(maxSpeed, scrollSpeed + 0.1);
                    }

                    // Friction
                    player.vx *= 0.9;
                    player.vx = Math.max(-5, Math.min(5, player.vx));

                    // Move player
                    player.x += player.vx;
                    player.x = Math.max(20, Math.min(GAME_WIDTH - 20, player.x));

                    // Calculate speed
                    gameState.speed = Math.round(scrollSpeed * 15);

                    // Update jumping physics
                    if (player.isJumping) {
                        player.jumpVelocity += 0.5; // Gravity
                        player.jumpHeight -= player.jumpVelocity;
                        
                        if (player.jumpHeight <= 0) {
                            player.jumpHeight = 0;
                            player.isJumping = false;
                            player.jumpVelocity = 0;
                            player.jumpsAvailable = player.maxJumps;
                            player.onGround = true;
                        } else {
                            player.onGround = false;
                        }
                    }

                    // Scroll elements
                    gates.forEach(gate => gate.y += scrollSpeed);
                    obstacles.forEach(obs => obs.y += scrollSpeed);
                    jumps.forEach(jump => jump.y += scrollSpeed);
                    trees.forEach(tree => tree.y += scrollSpeed);
                    enemies.forEach(enemy => enemy.y += scrollSpeed);
                    coins.forEach(coin => coin.y += scrollSpeed);
                    gems.forEach(gem => gem.y += scrollSpeed);
                    powerUps.forEach(powerUp => powerUp.y += scrollSpeed);

                    // Update enemies
                    enemies.forEach(enemy => {
                        if (enemy.zigzag) {
                            enemy.zigzagTimer++;
                            if (enemy.zigzagTimer > 60) {
                                enemy.vx = -enemy.vx;
                                enemy.zigzagTimer = 0;
                            }
                        }
                        enemy.x += enemy.vx;
                        enemy.x = Math.max(30, Math.min(GAME_WIDTH - 30, enemy.x));
                        
                        if (enemy.x <= 30 || enemy.x >= GAME_WIDTH - 30) {
                            enemy.vx = -enemy.vx;
                        }
                    });

                    // Check gate collision
                    gates.forEach(gate => {
                        if (!gate.passed && gate.y > player.y - 50 && gate.y < player.y + 50) {
                            const gateLeft = gate.x - gate.width / 2;
                            const gateRight = gate.x + gate.width / 2;
                            if (player.x > gateLeft && player.x < gateRight) {
                                gate.passed = true;
                                gameState.gatesPassed++;
                                gameState.score += 100 + gameState.slopeLevel * 50;
                                createParticle(gate.x, gate.y, '#00FF00', 10);
                            } else if (gate.y > player.y) {
                                gate.passed = true;
                                gameState.score = Math.max(0, gameState.score - 50);
                            }
                        }
                    });

                    // Check coin collection
                    coins.forEach(coin => {
                        if (!coin.collected && checkCollision(player, coin, 15, coin.radius)) {
                            coin.collected = true;
                            gameState.coins++;
                            gameState.score += 50;
                            createParticle(coin.x, coin.y, '#FFD700', 8);
                        }
                        coin.rotation += 0.1;
                    });

                    // Check gem collection
                    gems.forEach(gem => {
                        if (!gem.collected && checkCollision(player, gem, 15, gem.radius)) {
                            gem.collected = true;
                            gameState.gems++;
                            gameState.score += 200;
                            createParticle(gem.x, gem.y, '#FF00FF', 12);
                        }
                        gem.sparkle += 0.15;
                    });

                    // Check power-up collection
                    powerUps.forEach(powerUp => {
                        if (!powerUp.collected && checkCollision(player, powerUp, 15, powerUp.radius)) {
                            powerUp.collected = true;
                            if (powerUp.type === 'invincible') {
                                gameState.invincible = true;
                                gameState.invincibleTimer = 300; // 5 seconds at 60fps
                                createParticle(powerUp.x, powerUp.y, '#00FFFF', 15);
                            } else if (powerUp.type === 'speedBoost') {
                                gameState.speedBoost = true;
                                gameState.speedBoostTimer = 240; // 4 seconds
                                createParticle(powerUp.x, powerUp.y, '#FF6600', 15);
                            }
                            gameState.score += 100;
                        }
                        powerUp.pulse += 0.1;
                    });

                    // Check jump collision
                    jumps.forEach(jump => {
                        if (player.onGround && Math.abs(player.x - jump.x) < jump.width / 2 &&
                            Math.abs(player.y - jump.y) < 20) {
                            player.isJumping = true;
                            player.jumpVelocity = -10;
                            player.onGround = false;
                            gameState.score += 50;
                            createParticle(jump.x, jump.y, '#FFFFFF', 10);
                        }
                    });

                    // Check obstacle collision
                    if (!gameState.invincible && player.jumpHeight === 0) {
                        obstacles.forEach(obs => {
                            if (checkCollision(player, obs, 15, obs.radius)) {
                                gameState.gameOver = true;
                                createParticle(player.x, player.y, '#FF0000', 20);
                            }
                        });
                    }

                    // Check enemy collision
                    if (!gameState.invincible) {
                        enemies.forEach(enemy => {
                            if (Math.abs(player.x - enemy.x) < 25 && Math.abs(player.y - enemy.y) < 30 && player.jumpHeight === 0) {
                                gameState.gameOver = true;
                                createParticle(player.x, player.y, '#FF0000', 20);
                            }
                        });
                    }

                    // Update particles
                    particles = particles.filter(p => {
                        p.x += p.vx;
                        p.y += p.vy;
                        p.vy += 0.2;
                        p.life--;
                        return p.life > 0;
                    });

                    // Update snowflakes
                    if (weather === 'snowing') {
                        snowflakes.forEach(flake => {
                            flake.y += flake.speed + scrollSpeed;
                            flake.x += Math.sin(flake.y * 0.01) * 0.5;
                            if (flake.y > GAME_HEIGHT) {
                                flake.y = -10;
                                flake.x = Math.random() * GAME_WIDTH;
                            }
                        });
                    }

                    // Check race completion
                    if (gameState.gatesPassed >= gameState.totalGates) {
                        gameState.raceComplete = true;
                        gameState.score += 500;
                        gameState.score += gameState.coins * 10;
                        gameState.score += gameState.gems * 50;
                    }

                    // Remove off-screen elements
                    gates = gates.filter(g => g.y < GAME_HEIGHT + 100);
                    obstacles = obstacles.filter(o => o.y < GAME_HEIGHT + 100);
                    jumps = jumps.filter(j => j.y < GAME_HEIGHT + 100);
                    trees = trees.filter(t => t.y < GAME_HEIGHT + 100);
                    enemies = enemies.filter(e => e.y < GAME_HEIGHT + 100);
                    coins = coins.filter(c => !c.collected && c.y < GAME_HEIGHT + 100);
                    gems = gems.filter(g => !g.collected && g.y < GAME_HEIGHT + 100);
                    powerUps = powerUps.filter(p => !p.collected && p.y < GAME_HEIGHT + 100);
                }

                // Draw function
                function draw() {
                    // Clear canvas
                    ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
                    ctx.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);

                    // Draw slope lines
                    ctx.strokeStyle = '#D0D0D0';
                    ctx.lineWidth = 2;
                    for (let i = 0; i < 10; i++) {
                        const y = (i * 100 + (Date.now() / 20) % 100) % GAME_HEIGHT;
                        ctx.beginPath();
                        ctx.moveTo(0, y);
                        ctx.lineTo(GAME_WIDTH, y);
                        ctx.stroke();
                    }

                    // Draw trees
                    trees.forEach(tree => {
                        ctx.fillStyle = '#2D5016';
                        ctx.beginPath();
                        ctx.moveTo(tree.x, tree.y - tree.size);
                        ctx.lineTo(tree.x - tree.size / 2, tree.y);
                        ctx.lineTo(tree.x + tree.size / 2, tree.y);
                        ctx.fill();
                        ctx.fillStyle = '#4A3C28';
                        ctx.fillRect(tree.x - 3, tree.y, 6, tree.size / 2);
                    });

                    // Draw gates
                    gates.forEach(gate => {
                        const color = gate.side === 'left' ? '#FF0000' : '#0000FF';
                        ctx.fillStyle = gate.passed ? '#888888' : color;
                        ctx.fillRect(gate.x - gate.width / 2 - 5, gate.y - 40, 10, 80);
                        ctx.fillRect(gate.x + gate.width / 2 - 5, gate.y - 40, 10, 80);
                        ctx.fillStyle = gate.passed ? '#AAAAAA' : (gate.side === 'left' ? '#FF6666' : '#6666FF');
                        ctx.fillRect(gate.x - gate.width / 2 + 5, gate.y - 30, gate.width - 10, 20);
                    });

                    // Draw coins
                    coins.forEach(coin => {
                        if (!coin.collected) {
                            ctx.save();
                            ctx.translate(coin.x, coin.y);
                            ctx.rotate(coin.rotation);
                            ctx.fillStyle = '#FFD700';
                            ctx.beginPath();
                            ctx.arc(0, 0, coin.radius, 0, Math.PI * 2);
                            ctx.fill();
                            ctx.fillStyle = '#FFA500';
                            ctx.beginPath();
                            ctx.arc(0, 0, coin.radius * 0.6, 0, Math.PI * 2);
                            ctx.fill();
                            ctx.restore();
                        }
                    });

                    // Draw gems
                    gems.forEach(gem => {
                        if (!gem.collected) {
                            ctx.save();
                            ctx.translate(gem.x, gem.y);
                            const sparkleSize = Math.abs(Math.sin(gem.sparkle)) * 5;
                            ctx.fillStyle = '#FF00FF';
                            ctx.beginPath();
                            ctx.moveTo(0, -gem.radius);
                            for (let i = 0; i < 8; i++) {
                                const angle = (i * Math.PI / 4);
                                const r = i % 2 === 0 ? gem.radius : gem.radius * 0.5;
                                ctx.lineTo(Math.cos(angle) * r, Math.sin(angle) * r);
                            }
                            ctx.closePath();
                            ctx.fill();
                            ctx.fillStyle = '#FFFFFF';
                            ctx.fillRect(-sparkleSize/2, -1, sparkleSize, 2);
                            ctx.fillRect(-1, -sparkleSize/2, 2, sparkleSize);
                            ctx.restore();
                        }
                    });

                    // Draw power-ups
                    powerUps.forEach(powerUp => {
                        if (!powerUp.collected) {
                            const pulseSize = Math.abs(Math.sin(powerUp.pulse)) * 3;
                            ctx.save();
                            ctx.translate(powerUp.x, powerUp.y);
                            if (powerUp.type === 'invincible') {
                                ctx.fillStyle = '#00FFFF';
                                ctx.strokeStyle = '#0088FF';
                            } else {
                                ctx.fillStyle = '#FF6600';
                                ctx.strokeStyle = '#FF0000';
                            }
                            ctx.lineWidth = 2;
                            ctx.beginPath();
                            ctx.arc(0, 0, powerUp.radius + pulseSize, 0, Math.PI * 2);
                            ctx.fill();
                            ctx.stroke();
                            ctx.fillStyle = '#FFFFFF';
                            ctx.font = 'bold 16px Arial';
                            ctx.textAlign = 'center';
                            ctx.textBaseline = 'middle';
                            ctx.fillText(powerUp.type === 'invincible' ? '⭐' : '⚡', 0, 0);
                            ctx.restore();
                        }
                    });

                    // Draw jumps
                    jumps.forEach(jump => {
                        ctx.fillStyle = '#FFFFFF';
                        ctx.beginPath();
                        ctx.moveTo(jump.x - jump.width / 2, jump.y);
                        ctx.lineTo(jump.x, jump.y - jump.height);
                        ctx.lineTo(jump.x + jump.width / 2, jump.y);
                        ctx.fill();
                    });

                    // Draw obstacles
                    obstacles.forEach(obs => {
                        ctx.fillStyle = '#666666';
                        ctx.beginPath();
                        ctx.arc(obs.x, obs.y, obs.radius, 0, Math.PI * 2);
                        ctx.fill();
                        ctx.fillStyle = '#999999';
                        ctx.beginPath();
                        ctx.arc(obs.x - obs.radius / 3, obs.y - obs.radius / 3, obs.radius / 3, 0, Math.PI * 2);
                        ctx.fill();
                    });

                    // Draw enemies (snowboarders)
                    enemies.forEach(enemy => {
                        ctx.fillStyle = enemy.color;
                        ctx.fillRect(enemy.x - enemy.width / 2, enemy.y - enemy.height / 2, enemy.width, enemy.height);
                        ctx.fillStyle = '#000000';
                        ctx.beginPath();
                        ctx.arc(enemy.x, enemy.y - enemy.height / 2 - 5, 6, 0, Math.PI * 2);
                        ctx.fill();
                        ctx.strokeStyle = '#333333';
                        ctx.lineWidth = 3;
                        ctx.beginPath();
                        ctx.moveTo(enemy.x - 12, enemy.y + enemy.height / 2 + 2);
                        ctx.lineTo(enemy.x + 12, enemy.y + enemy.height / 2 + 2);
                        ctx.stroke();
                    });

                    // Draw particles
                    particles.forEach(p => {
                        ctx.fillStyle = p.color;
                        ctx.globalAlpha = p.life / 30;
                        ctx.beginPath();
                        ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
                        ctx.fill();
                        ctx.globalAlpha = 1;
                    });

                    // Draw player (skier)
                    const playerDrawY = player.y - player.jumpHeight;
                    
                    // Shadow when jumping
                    if (player.jumpHeight > 0) {
                        ctx.fillStyle = 'rgba(0, 0, 0, 0.3)';
                        ctx.beginPath();
                        ctx.ellipse(player.x, player.y, 15, 8, 0, 0, Math.PI * 2);
                        ctx.fill();
                    }

                    // Invincible glow
                    if (gameState.invincible) {
                        ctx.strokeStyle = '#00FFFF';
                        ctx.lineWidth = 3;
                        ctx.globalAlpha = 0.5 + Math.abs(Math.sin(Date.now() / 100)) * 0.5;
                        ctx.beginPath();
                        ctx.arc(player.x, playerDrawY, 25, 0, Math.PI * 2);
                        ctx.stroke();
                        ctx.globalAlpha = 1;
                    }

                    // Speed boost trail
                    if (gameState.speedBoost) {
                        ctx.strokeStyle = '#FF6600';
                        ctx.lineWidth = 4;
                        ctx.globalAlpha = 0.3;
                        ctx.beginPath();
                        ctx.moveTo(player.x - 10, playerDrawY + 20);
                        ctx.lineTo(player.x - 10, playerDrawY + 40);
                        ctx.moveTo(player.x + 10, playerDrawY + 20);
                        ctx.lineTo(player.x + 10, playerDrawY + 40);
                        ctx.stroke();
                        ctx.globalAlpha = 1;
                    }

                    // Skier body
                    ctx.fillStyle = '#FF6600';
                    ctx.fillRect(player.x - 10, playerDrawY - 15, 20, 25);
                    
                    // Helmet
                    ctx.fillStyle = '#0066CC';
                    ctx.beginPath();
                    ctx.arc(player.x, playerDrawY - 20, 8, 0, Math.PI * 2);
                    ctx.fill();

                    // Skis
                    ctx.fillStyle = '#333333';
                    const skiAngle = player.vx * 0.1;
                    ctx.save();
                    ctx.translate(player.x, playerDrawY + 15);
                    ctx.rotate(skiAngle);
                    ctx.fillRect(-15, -2, 30, 4);
                    ctx.restore();

                    // Carving effect
                    if (player.carving && player.jumpHeight === 0) {
                        ctx.strokeStyle = '#FFFFFF';
                        ctx.lineWidth = 3;
                        ctx.beginPath();
                        ctx.moveTo(player.x - 15, player.y + 20);
                        ctx.lineTo(player.x - 15, player.y + 40);
                        ctx.moveTo(player.x + 15, player.y + 20);
                        ctx.lineTo(player.x + 15, player.y + 40);
                        ctx.stroke();
                    }

                    // Jump indicator
                    if (player.jumpsAvailable < player.maxJumps) {
                        ctx.fillStyle = '#FFFFFF';
                        ctx.font = 'bold 12px Arial';
                        ctx.textAlign = 'center';
                        ctx.fillText('Jumps: ' + player.jumpsAvailable, player.x, playerDrawY - 35);
                    }

                    // Draw snowflakes
                    if (weather === 'snowing') {
                        ctx.fillStyle = '#FFFFFF';
                        snowflakes.forEach(flake => {
                            ctx.beginPath();
                            ctx.arc(flake.x, flake.y, flake.size, 0, Math.PI * 2);
                            ctx.fill();
                        });
                    }

                    // Game over / Race complete
                    if (gameState.gameOver) {
                        ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
                        ctx.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
                        ctx.fillStyle = '#FF0000';
                        ctx.font = 'bold 36px Arial';
                        ctx.textAlign = 'center';
                        ctx.fillText('CRASHED!', GAME_WIDTH / 2, GAME_HEIGHT / 2 - 60);
                        ctx.fillStyle = '#FFFFFF';
                        ctx.font = '20px Arial';
                        ctx.fillText('Score: ' + gameState.score, GAME_WIDTH / 2, GAME_HEIGHT / 2 - 20);
                        ctx.fillText('💰 ' + gameState.coins + '  💎 ' + gameState.gems, GAME_WIDTH / 2, GAME_HEIGHT / 2 + 10);
                        ctx.fillText('Tap/Press R to Restart', GAME_WIDTH / 2, GAME_HEIGHT / 2 + 50);
                    }

                    if (gameState.raceComplete) {
                        ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
                        ctx.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
                        ctx.fillStyle = '#00FF00';
                        ctx.font = 'bold 36px Arial';
                        ctx.textAlign = 'center';
                        ctx.fillText('LEVEL COMPLETE!', GAME_WIDTH / 2, GAME_HEIGHT / 2 - 80);
                        ctx.fillStyle = '#FFFFFF';
                        ctx.font = '18px Arial';
                        ctx.fillText('Score: ' + gameState.score, GAME_WIDTH / 2, GAME_HEIGHT / 2 - 40);
                        ctx.fillText('Gates: ' + gameState.gatesPassed + '/' + gameState.totalGates, GAME_WIDTH / 2, GAME_HEIGHT / 2 - 10);
                        ctx.fillText('💰 ' + gameState.coins + '  💎 ' + gameState.gems, GAME_WIDTH / 2, GAME_HEIGHT / 2 + 20);
                        
                        if (gameState.slopeLevel < 3) {
                            ctx.fillText('Tap/Press N for Next Level', GAME_WIDTH / 2, GAME_HEIGHT / 2 + 60);
                        } else {
                            ctx.fillText('YOU CONQUERED ALL SLOPES!', GAME_WIDTH / 2, GAME_HEIGHT / 2 + 60);
                        }
                        ctx.fillText('Tap/Press R to Restart', GAME_WIDTH / 2, GAME_HEIGHT / 2 + 90);
                    }

                    // Update UI
                    const scoreEl = document.getElementById('score');
                    const speedEl = document.getElementById('speed');
                    const gatesEl = document.getElementById('gates');
                    const coinsEl = document.getElementById('coins');
                    const gemsEl = document.getElementById('gems');
                    const levelEl = document.getElementById('level');
                    const slopeEl = document.getElementById('slope');
                    
                    if (scoreEl) scoreEl.textContent = 'Score: ' + gameState.score;
                    if (speedEl) speedEl.textContent = 'Speed: ' + gameState.speed + ' km/h';
                    if (gatesEl) gatesEl.textContent = 'Gates: ' + gameState.gatesPassed + '/' + gameState.totalGates;
                    if (coinsEl) coinsEl.textContent = '💰 Coins: ' + gameState.coins;
                    if (gemsEl) gemsEl.textContent = '💎 Gems: ' + gameState.gems;
                    if (levelEl) levelEl.textContent = 'Level: ' + gameState.level;
                    if (slopeEl) slopeEl.textContent = 'Slope: ' + gameState.slopeNames[gameState.slopeLevel];
                }

                // Restart
                function restart() {
                    gameState.score = 0;
                    gameState.coins = 0;
                    gameState.gems = 0;
                    gameState.level = 1;
                    gameState.slopeLevel = 0;
                    gameState.invincible = false;
                    gameState.speedBoost = false;
                    initLevel();
                }

                // Next level
                function nextLevel() {
                    if (gameState.slopeLevel < 3) {
                        gameState.slopeLevel++;
                    }
                    gameState.level++;
                    gameState.totalGates += 5;
                    player.baseSpeed = 3 + gameState.slopeLevel * 0.5;
                    initLevel();
                }

                // Keyboard restart/next
                window.addEventListener('keydown', (e) => {
                    if (e.key === 'r' || e.key === 'R') {
                        restart();
                    }
                    if ((e.key === 'n' || e.key === 'N') && gameState.raceComplete) {
                        nextLevel();
                    }
                });

                // Touch restart/next
                canvas.addEventListener('touchend', (e) => {
                    if (gameState.gameOver) {
                        restart();
                    } else if (gameState.raceComplete) {
                        nextLevel();
                    }
                });

                // Game loop
                function gameLoop() {
                    update();
                    draw();
                    requestAnimationFrame(gameLoop);
                }

                gameLoop();

            } catch(e) {
                document.body.innerHTML = '<div style="color:white;padding:20px;text-align:center;">Game error: ' + e.message + '</div>';
                console.error(e);
            }
        });
    </script>
</body>
</html>

Improving Your Game...

Analyzing your request...

This usually takes 1-3 minutes

📱

Install GB3A

Install GB3A on your device for a better experience!