One Shot 3D Games You Can Test Immediately using Three.JS Grok and Claude

Community Article Published April 17, 2025

I found myself wondering if I should try yet again for multiplayer games while rolling the dice and trying to write a few examples then upgrading them in my style knowledge having played everything over the years.

First try - quite a worthy attempt: https://huggingface.co/spaces/awacke1/GameOneShotsWithThreeJS

image/png

Grok


I'll create a simple Pac-Man style game using HTML5 canvas and JavaScript. The game will include a player (Pac-Man), a maze, collectible dots, and basic movement controls.

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Pac```html
ac-Man Game</title>
    <style>
        canvas {
            border: 1px solid black;
            display: block;
            margin: 0 auto;
            background-color: #000;
        }
        body {
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            background-color: #f0f0f0;
        }
        #score {
            position: absolute;
            top: 10px;
            left: 10px;
            font-family: Arial, sans-serif;
            font-size: 24px;
            color: #000;
        }
    </style>
</head>
<body>
    <div id="score">Score: 0</div>
    <canvas id="gameCanvas" width="448" height="496"></canvas>
    <script>
        const canvas = document.getElementById('gameCanvas');
        const ctx = canvas.getContext('2d');
        const tileSize = 16;
        const map = [
            [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
            [1,2,2,2,2,2,2,2,2,2,2,2,2,1,1,2,2,2,2,2,2,2,2,2,2,2,2,1],
            [1,2,1,1,1,1,2,1,1,1,1,1,2,1,1,2,1,1,1,1,1,2,1,1,1,1,2,1],
            [1,2,1,0,0,1,2,1,0,0,0,1,2,1,1,2,1,0,0,0,1,2,1,0,0,1,2,1],
            [1,2,1,1,1,1,2,1,1,1,1,1,2,1,1,2,1,1,1,1,1,2,1,1,1,1,2,1],
            [1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1],
            [1,2,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,2,1],
            [1,2,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,2,1],
            [1,2,2,2,2,2,2,1,1,2,2,2,2,1,1,2,2,2,2,1,1,2,2,2,2,2,2,1],
            [1,1,1,1,1,1,2,1,1,2,1,1,1,1,1,1,1,1,2,1,1,2,1,1,1,1,1,1],
            [0,0,0,0,0,1,2,1,1,2,1,0,0,0,0,0,0,0,2,1,1,2,1,0,0,0,0,0],
            [0,0,0,0,0,1,2,1,1,2,1,0,0,0,0,0,0,0,2,1,1,2,1,0,0,0,0,0],
            [1,1,1,1,1,1,2,1,1,2,1,0,0,0,0,0,0,0,2,1,1,2,1,1,1,1,1,1],
            [2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2],
            [1,1,1,1,1,1,2,1,1,2,1,0,0,0,0,0,0,0,2,1,1,2,1,1,1,1,1,1],
            [0,0,0,0,0,1,2,1,1,2,1,0,0,0,0,0,0,0,2,1,1,2,1,0,0,0,0,0],
            [0,0,0,0,0,1,2,1,1,2,1,0,0,0,0,0,0,0,2,1,1,2,1,0,0,0,0,0],
            [1,1,1,1,1,1,2,1,1,2,1,1,1,1,1,1,1,1,2,1,1,2,1,1,1,1,1,1],
            [1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1],
            [1,2,1,1,1,1,2,1,1,1,1,1,2,1,1,2,1,1,1,1,1,2,1,1,1,1,2,1],
            [1,2,1,1,1,1,2,1,1,1,1,1,2,1,1,2,1,1,1,1,1,2,1,1,1,1,2,1],
            [1,2,2,2,2,2,2,1,1,2,2,2,2,2,2,2,2,2,2,1,1,2,2,2,2,2,2,1],
            [1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,2,1],
            [1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,2,1],
            [1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1],
            [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
        ];

        let player = {
            x: 14,
            y: 23,
            direction: 'right',
            nextDirection: 'right',
            speed: 0.1,
            mouthAngle: 0,
            mouthSpeed: 0.1
        };

        let score = 0;
        const scoreDisplay = document.getElementById('score');

        function drawMap() {
            for (let y = 0; y < map.length; y++) {
                for (let x = 0; x < map[y].length; x++) {
                    if (map[y][x] === 1) {
                        ctx.fillStyle = 'blue';
                        ctx.fillRect(x * tileSize, y * tileSize, tileSize, tileSize);
                    } else if (map[y][x] === 2) {
                        ctx.fillStyle = 'yellow';
                        ctx.beginPath();
                        ctx.arc(x * tileSize + tileSize / 2, y * tileSize + tileSize / 2, 2, 0, Math.PI * 2);
                        ctx.fill();
                    }
                }
            }
        }

        function drawPlayer() {
            ctx.fillStyle = 'yellow';
            ctx.beginPath();
            const centerX = player.x * tileSize + tileSize / 2;
            const centerY = player.y * tileSize + tileSize / 2;
            let startAngle, endAngle;

            if (player.direction === 'right') {
                startAngle = player.mouthAngle;
                endAngle = 2 * Math.PI - player.mouthAngle;
            } else if (player.direction === 'left') {
                startAngle = Math.PI + player.mouthAngle;
                endAngle = Math.PI - player.mouthAngle;
            } else if (player.direction === 'up') {
                startAngle = Math.PI * 1.5 + player.mouthAngle;
                endAngle = Math.PI * 1.5 - player.mouthAngle;
            } else {
                startAngle = Math.PI / 2 + player.mouthAngle;
                endAngle = Math.PI / 2 - player.mouthAngle;
            }

            ctx.arc(centerX, centerY, tileSize / 2, startAngle, endAngle);
            ctx.lineTo(centerX, centerY);
            ctx.fill();
            player.mouthAngle += player.mouthSpeed;
            if (player.mouthAngle > Math.PI / 4 || player.mouthAngle < 0) {
                player.mouthSpeed = -player.mouthSpeed;
            }
        }

        function canMove(x, y) {
            const tileX = Math.floor(x);
            const tileY = Math.floor(y);
            if (tileX < 0 || tileX >= map[0].length || tileY < 0 || tileY >= map.length) return false;
            return map[tileY][tileX] !== 1;
        }

        function collectDot() {
            const tileX = Math.floor(player.x);
            const tileY = Math.floor(player.y);
            if (map[tileY][tileX] === 2) {
                map[tileY][tileX] = 0;
                score += 10;
                scoreDisplay.textContent = `Score: ${score}`;
            }
        }

        function updatePlayer() {
            let nextX = player.x;
            let nextY = player.y;

            if (player.nextDirection === 'right' && canMove(player.x + player.speed, player.y)) {
                player.direction = 'right';
                nextX += player.speed;
            } else if (player.nextDirection === 'left' && canMove(player.x - player.speed, player.y)) {
                player.direction = 'left';
                nextX -= player.speed;
            } else if (player.nextDirection === 'up' && canMove(player.x, player.y - player.speed)) {
                player.direction = 'up';
                nextY -= player.speed;
            } else if (player.nextDirection === 'down' && canMove(player.x, player.y + player.speed)) {
                player.direction = 'down';
                nextY += player.speed;
            }

            if (player.direction === 'right' && canMove(nextX + player.speed, player.y)) {
                nextX += player.speed;
            } else if (player.direction === 'left' && canMove(nextX - player.speed, player.y)) {
                nextX -= player.speed;
            } else if (player.direction === 'up' && canMove(player.x, nextY - player.speed)) {
                nextY -= player.speed;
            } else if (player.direction === 'down' && canMove(player.x, nextY + player.speed)) {
                nextY += player.speed;
            }

            player.x = nextX;
            player.y = nextY;
            collectDot();
        }

        document.addEventListener('keydown', (e) => {
            switch (e.key) {
                case 'ArrowRight':
                    player.nextDirection = 'right';
                    break;
                case 'ArrowLeft':
                    player.nextDirection = 'left';
                    break;
                case 'ArrowUp':
                    player.nextDirection = 'up';
                    break;
                case 'ArrowDown':
                    player.nextDirection = 'down';
                    break;
            }
        });

        function gameLoop() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            drawMap();
            updatePlayer();
            drawPlayer();
            requestAnimationFrame(gameLoop);
        }

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

And then trying a second version educated on the first.

Quite a great second go with Claude to one-shot a great 3D in same tech which is immediately verifiable.

image/png

Code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>3D Pac-Man Game</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
            background-color: #000;
            font-family: Arial, sans-serif;
        }
        #info {
            position: absolute;
            top: 10px;
            left: 10px;
            color: white;
            font-size: 16px;
            background-color: rgba(0, 0, 0, 0.5);
            padding: 10px;
            border-radius: 5px;
        }
        #gameOverScreen {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background-color: rgba(0, 0, 0, 0.8);
            color: white;
            padding: 20px;
            border-radius: 10px;
            text-align: center;
            display: none;
        }
        #gameOverScreen button {
            margin-top: 20px;
            padding: 10px 20px;
            background-color: yellow;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            font-weight: bold;
        }
        #instructions {
            position: absolute;
            bottom: 10px;
            left: 10px;
            color: white;
            font-size: 14px;
            background-color: rgba(0, 0, 0, 0.5);
            padding: 10px;
            border-radius: 5px;
        }
    </style>
</head>
<body>
    <div id="info">Score: <span id="score">0</span> | Dots Left: <span id="dotsLeft">0</span></div>
    <div id="instructions">Controls: Arrow Keys to move, WASD to rotate camera, Q/E to raise/lower camera</div>
    <div id="gameOverScreen">
        <h2>Game Over</h2>
        <p>Your final score: <span id="finalScore">0</span></p>
        <button id="restartButton">Play Again</button>
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script>
        // Game variables
        let scene, camera, renderer, player, maze = [];
        let score = 0, dotsLeft = 0;
        let gameOver = false;
        let cameraOffset = { x: 0, y: 5, z: 10 };
        let cameraRotation = { x: -0.5, y: 0, z: 0 };

        // Maze layout (1: wall, 2: dot, 0: empty space)
        const map = [
            [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
            [1,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,1],
            [1,2,1,1,2,1,1,1,2,1,2,1,1,1,2,1,1,2,1],
            [1,2,1,1,2,1,1,1,2,1,2,1,1,1,2,1,1,2,1],
            [1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1],
            [1,2,1,1,2,1,2,1,1,1,1,1,2,1,2,1,1,2,1],
            [1,2,2,2,2,1,2,2,2,1,2,2,2,1,2,2,2,2,1],
            [1,1,1,1,2,1,1,1,0,1,0,1,1,1,2,1,1,1,1],
            [0,0,0,1,2,1,0,0,0,0,0,0,0,1,2,1,0,0,0],
            [1,1,1,1,2,1,0,1,1,0,1,1,0,1,2,1,1,1,1],
            [0,0,0,0,2,0,0,1,0,0,0,1,0,0,2,0,0,0,0],
            [1,1,1,1,2,1,0,1,1,1,1,1,0,1,2,1,1,1,1],
            [0,0,0,1,2,1,0,0,0,0,0,0,0,1,2,1,0,0,0],
            [1,1,1,1,2,1,0,1,1,1,1,1,0,1,2,1,1,1,1],
            [1,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,1],
            [1,2,1,1,2,1,1,1,2,1,2,1,1,1,2,1,1,2,1],
            [1,2,2,1,2,2,2,2,2,0,2,2,2,2,2,1,2,2,1],
            [1,1,2,1,2,1,2,1,1,1,1,1,2,1,2,1,2,1,1],
            [1,2,2,2,2,1,2,2,2,1,2,2,2,1,2,2,2,2,1],
            [1,2,1,1,1,1,1,1,2,1,2,1,1,1,1,1,1,2,1],
            [1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1],
            [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
        ];

        // Initialize the game
        function init() {
            // Reset game variables
            score = 0;
            document.getElementById('score').textContent = score;
            gameOver = false;
            document.getElementById('gameOverScreen').style.display = 'none';
            maze = [];
            dotsLeft = 0;

            // Create a scene
            scene = new THREE.Scene();
            scene.background = new THREE.Color(0x000000);

            // Create a camera
            camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
            camera.position.set(cameraOffset.x, cameraOffset.y, cameraOffset.z);
            camera.rotation.x = cameraRotation.x;

            // Create a renderer
            renderer = new THREE.WebGLRenderer({ antialias: true });
            renderer.setSize(window.innerWidth, window.innerHeight);
            if (document.querySelector('canvas')) {
                document.body.removeChild(document.querySelector('canvas'));
            }
            document.body.appendChild(renderer.domElement);

            // Add lights
            const ambientLight = new THREE.AmbientLight(0x404040, 1);
            scene.add(ambientLight);

            const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
            directionalLight.position.set(0, 20, 10);
            scene.add(directionalLight);

            // Create the maze
            createMaze();

            // Create player (Pac-Man)
            createPlayer();

            // Add event listeners
            window.addEventListener('resize', onWindowResize);
            window.addEventListener('keydown', onKeyDown);
            document.getElementById('restartButton').addEventListener('click', init);

            // Start animation loop
            animate();
        }

        function createMaze() {
            // Ground
            const groundGeometry = new THREE.PlaneGeometry(map[0].length + 2, map.length + 2);
            const groundMaterial = new THREE.MeshLambertMaterial({ color: 0x333333, side: THREE.DoubleSide });
            const ground = new THREE.Mesh(groundGeometry, groundMaterial);
            ground.rotation.x = Math.PI / 2;
            ground.position.set(map[0].length / 2 - 0.5, -0.5, map.length / 2 - 0.5);
            scene.add(ground);

            // Create maze elements
            for (let z = 0; z < map.length; z++) {
                maze[z] = [];
                for (let x = 0; x < map[z].length; x++) {
                    if (map[z][x] === 1) {
                        // Create wall
                        const wallGeometry = new THREE.BoxGeometry(1, 1, 1);
                        const wallMaterial = new THREE.MeshLambertMaterial({ color: 0x0000ff });
                        const wall = new THREE.Mesh(wallGeometry, wallMaterial);
                        wall.position.set(x, 0, z);
                        scene.add(wall);
                        maze[z][x] = { type: 'wall', object: wall };
                    } else if (map[z][x] === 2) {
                        // Create dot
                        const dotGeometry = new THREE.SphereGeometry(0.1, 8, 8);
                        const dotMaterial = new THREE.MeshLambertMaterial({ color: 0xffff00 });
                        const dot = new THREE.Mesh(dotGeometry, dotMaterial);
                        dot.position.set(x, 0, z);
                        scene.add(dot);
                        maze[z][x] = { type: 'dot', object: dot, collected: false };
                        dotsLeft++;
                    } else {
                        maze[z][x] = { type: 'empty' };
                    }
                }
            }
            
            document.getElementById('dotsLeft').textContent = dotsLeft;
        }

        function createPlayer() {
            // Create Pac-Man body (sphere with mouth)
            const playerGeometry = new THREE.SphereGeometry(0.4, 32, 32, 0, Math.PI * 1.8);
            const playerMaterial = new THREE.MeshLambertMaterial({ color: 0xffff00 });
            player = new THREE.Mesh(playerGeometry, playerMaterial);
            
            // Set initial position (middle of the maze)
            player.position.set(9, 0, 10);
            player.rotation.y = Math.PI / 2; // Face right initially
            scene.add(player);
            
            // Player properties
            player.userData = {
                direction: { x: 0, z: 0 },
                nextDirection: { x: 0, z: 0 },
                speed: 0.05,
                mouthOpen: true,
                mouthSpeed: 0.05
            };
        }

        function movePlayer() {
            if (gameOver) return;
            
            // Try to move in the next direction if requested
            if (player.userData.nextDirection.x !== 0 || player.userData.nextDirection.z !== 0) {
                const nextX = Math.floor(player.position.x + player.userData.nextDirection.x * player.userData.speed * 2);
                const nextZ = Math.floor(player.position.z + player.userData.nextDirection.z * player.userData.speed * 2);
                
                // Check if the next position is valid
                if (nextX >= 0 && nextX < map[0].length && 
                    nextZ >= 0 && nextZ < map.length && 
                    map[nextZ][nextX] !== 1) {
                    player.userData.direction = {...player.userData.nextDirection};
                    
                    // Rotate player to face the direction of movement
                    if (player.userData.direction.x === 1) player.rotation.y = Math.PI / 2;
                    else if (player.userData.direction.x === -1) player.rotation.y = -Math.PI / 2;
                    else if (player.userData.direction.z === 1) player.rotation.y = Math.PI;
                    else if (player.userData.direction.z === -1) player.rotation.y = 0;
                }
            }
            
            // Move player in the current direction
            if (player.userData.direction.x !== 0 || player.userData.direction.z !== 0) {
                const newX = player.position.x + player.userData.direction.x * player.userData.speed;
                const newZ = player.position.z + player.userData.direction.z * player.userData.speed;
                
                // Check if we're about to hit a wall
                const nextTileX = Math.floor(newX + player.userData.direction.x * 0.4);
                const nextTileZ = Math.floor(newZ + player.userData.direction.z * 0.4);
                
                if (nextTileX >= 0 && nextTileX < map[0].length && 
                    nextTileZ >= 0 && nextTileZ < map.length && 
                    map[nextTileZ][nextTileX] !== 1) {
                    player.position.x = newX;
                    player.position.z = newZ;
                    
                    // Tunnel teleport (if you go off one side, appear on the other)
                    if (player.position.x < 0) player.position.x = map[0].length - 1;
                    if (player.position.x >= map[0].length) player.position.x = 0;
                    if (player.position.z < 0) player.position.z = map.length - 1;
                    if (player.position.z >= map.length) player.position.z = 0;
                    
                    // Collect dots
                    collectDots();
                }
            }
            
            // Animate Pac-Man's mouth
            animateMouth();
            
            // Update camera position
            updateCamera();
        }

        function animateMouth() {
            // Create the mouth animation effect
            const playerGeometry = player.geometry;
            if (player.userData.mouthOpen) {
                playerGeometry.parameters.thetaLength -= player.userData.mouthSpeed;
                if (playerGeometry.parameters.thetaLength <= Math.PI) {
                    player.userData.mouthOpen = false;
                }
            } else {
                playerGeometry.parameters.thetaLength += player.userData.mouthSpeed;
                if (playerGeometry.parameters.thetaLength >= Math.PI * 1.8) {
                    player.userData.mouthOpen = true;
                }
            }
            player.geometry = new THREE.SphereGeometry(
                0.4, 32, 32, 0, playerGeometry.parameters.thetaLength
            );
            player.material = new THREE.MeshLambertMaterial({ color: 0xffff00 });
        }

        function collectDots() {
            const x = Math.round(player.position.x);
            const z = Math.round(player.position.z);
            
            // Check if there's a dot at the player's position
            if (x >= 0 && x < map[0].length && z >= 0 && z < map.length) {
                if (maze[z][x] && maze[z][x].type === 'dot' && !maze[z][x].collected) {
                    // Collect the dot
                    maze[z][x].collected = true;
                    scene.remove(maze[z][x].object);
                    
                    // Update score
                    score += 10;
                    document.getElementById('score').textContent = score;
                    
                    // Update dots left
                    dotsLeft--;
                    document.getElementById('dotsLeft').textContent = dotsLeft;
                    
                    // Check if all dots are collected
                    if (dotsLeft === 0) {
                        // Player wins
                        gameOver = true;
                        document.getElementById('gameOverScreen').style.display = 'block';
                        document.getElementById('finalScore').textContent = score;
                    }
                }
            }
        }

        function updateCamera() {
            // Camera follows player with offset
            camera.position.x = player.position.x + cameraOffset.x;
            camera.position.y = player.position.y + cameraOffset.y;
            camera.position.z = player.position.z + cameraOffset.z;
            
            // Look at player
            camera.lookAt(player.position.x, player.position.y, player.position.z);
        }

        function onKeyDown(event) {
            if (gameOver) return;
            
            // Movement keys
            switch (event.key) {
                case 'ArrowUp':
                    player.userData.nextDirection = { x: 0, z: -1 };
                    break;
                case 'ArrowDown':
                    player.userData.nextDirection = { x: 0, z: 1 };
                    break;
                case 'ArrowLeft':
                    player.userData.nextDirection = { x: -1, z: 0 };
                    break;
                case 'ArrowRight':
                    player.userData.nextDirection = { x: 1, z: 0 };
                    break;
                
                // Camera controls
                case 'w':
                    cameraOffset.y += 0.5;
                    break;
                case 's':
                    cameraOffset.y -= 0.5;
                    break;
                case 'a':
                    cameraOffset.x -= 0.5;
                    break;
                case 'd':
                    cameraOffset.x += 0.5;
                    break;
                case 'q':
                    cameraOffset.z -= 0.5;
                    break;
                case 'e':
                    cameraOffset.z += 0.5;
                    break;
            }
        }

        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        }

        function animate() {
            requestAnimationFrame(animate);
            movePlayer();
            renderer.render(scene, camera);
        }

        // Start the game
        init();
    </script>
</body>
</html>

Final Space with Code:

https://huggingface.co/spaces/awacke1/GameOneShotsWithThreeJS

I find the magic of Three.JS indisputable for an amazing effect of using 3D.

Enjoy!

<video controls autoplay src="

">

Aaron

Community

Your need to confirm your account before you can post a new comment.

Sign up or log in to comment