Skip to content

Instantly share code, notes, and snippets.

@nonetrix
Last active November 19, 2025 08:28
Show Gist options
  • Select an option

  • Save nonetrix/daa8b6233176b740aead594f99e427d2 to your computer and use it in GitHub Desktop.

Select an option

Save nonetrix/daa8b6233176b740aead594f99e427d2 to your computer and use it in GitHub Desktop.
vibe coded game so bad that crashes Mesa somehow
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<title>Touch Voxel Clone</title>
<style>
body { margin: 0; overflow: hidden; background-color: #87CEEB; font-family: sans-serif; touch-action: none; user-select: none; -webkit-user-select: none; }
canvas { display: block; }
/* UI Overlay */
#ui-layer { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; }
/* Crosshair */
#crosshair {
position: absolute; top: 50%; left: 50%;
width: 20px; height: 20px;
background: transparent;
border: 2px solid rgba(255, 255, 255, 0.8);
border-radius: 50%;
transform: translate(-50%, -50%);
}
#crosshair::after {
content: ''; position: absolute; top: 50%; left: 50%;
width: 4px; height: 4px; background: white;
transform: translate(-50%, -50%);
}
/* Touch Zones */
.control-area { pointer-events: auto; position: absolute; bottom: 20px; }
#stick-container { left: 20px; width: 120px; height: 120px; background: rgba(255,255,255,0.1); border-radius: 50%; border: 2px solid rgba(255,255,255,0.2); }
#stick-nub { position: relative; top: 50%; left: 50%; width: 50px; height: 50px; background: rgba(255,255,255,0.5); border-radius: 50%; transform: translate(-50%, -50%); pointer-events: none; }
/* Action Buttons */
#actions { right: 20px; display: flex; flex-direction: column; gap: 15px; align-items: flex-end; }
.btn {
width: 70px; height: 70px;
background: rgba(0, 0, 0, 0.3);
border: 2px solid rgba(255, 255, 255, 0.4);
border-radius: 15px;
color: white; font-weight: bold;
display: flex; justify-content: center; align-items: center;
font-size: 14px; text-transform: uppercase;
pointer-events: auto;
backdrop-filter: blur(4px);
}
.btn:active { background: rgba(255, 255, 255, 0.3); }
.active-mode { background: rgba(0, 255, 0, 0.4); border-color: #0f0; }
#debug { position: absolute; top: 10px; left: 10px; color: white; text-shadow: 1px 1px 0 #000; font-size: 12px; }
</style>
<!-- Import Map for Three.js -->
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/[email protected]/build/three.module.js",
"three/addons/": "https://unpkg.com/[email protected]/examples/jsm/"
}
}
</script>
</head>
<body>
<div id="ui-layer">
<div id="debug">Loading World...</div>
<div id="crosshair"></div>
<!-- Left Joystick -->
<div id="stick-container" class="control-area">
<div id="stick-nub"></div>
</div>
<!-- Right Buttons -->
<div id="actions" class="control-area">
<div id="btn-mode" class="btn">Mode: Break</div>
<div id="btn-jump" class="btn">Jump</div>
</div>
</div>
<script type="module">
import * as THREE from 'three';
// --- Configuration ---
const WORLD_SIZE = 32;
const WORLD_HEIGHT = 16;
const BLOCK_SIZE = 1;
// --- Globals ---
let camera, scene, renderer;
let raycaster;
// FIX: Define lastTime here so it exists before animate() is called
let lastTime = performance.now();
// Physics
const player = {
position: new THREE.Vector3(WORLD_SIZE/2, WORLD_HEIGHT + 5, WORLD_SIZE/2),
velocity: new THREE.Vector3(),
speed: 6.0,
jumpForce: 10.0,
gravity: 25.0,
height: 1.6,
radius: 0.3,
grounded: false
};
// Voxel Data
const voxels = {};
let instancedMesh;
const maxInstances = WORLD_SIZE * WORLD_SIZE * WORLD_HEIGHT;
let instanceCount = 0;
const dummyMatrix = new THREE.Object3D();
// Inputs
const input = {
moveX: 0,
moveY: 0,
jump: false,
placeMode: false
};
// Touch State
const touches = {
moveId: null,
lookId: null,
startX: 0, startY: 0,
lastLookX: 0, lastLookY: 0,
tapStartTime: 0,
isTap: false
};
// Start Game
init();
animate();
function init() {
// 1. Scene Setup
scene = new THREE.Scene();
scene.background = new THREE.Color(0x87CEEB);
scene.fog = new THREE.Fog(0x87CEEB, 10, 50);
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
// 2. Lighting
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);
const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
dirLight.position.set(50, 100, 50);
scene.add(dirLight);
// 3. Texture Generation
const texture = createBlockTexture();
const material = new THREE.MeshLambertMaterial({ map: texture });
const geometry = new THREE.BoxGeometry(BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
// 4. Instanced Mesh
instancedMesh = new THREE.InstancedMesh(geometry, material, maxInstances);
instancedMesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
scene.add(instancedMesh);
// 5. World Generation
generateWorld();
rebuildMesh();
// 6. Renderer
renderer = new THREE.WebGLRenderer({ antialias: false });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 7. Raycaster
raycaster = new THREE.Raycaster();
raycaster.far = 6;
// 8. Input Listeners
setupTouchControls();
window.addEventListener('resize', onWindowResize);
}
function createBlockTexture() {
const canvas = document.createElement('canvas');
canvas.width = 64; canvas.height = 64;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#795548';
ctx.fillRect(0,0,64,64);
ctx.fillStyle = '#4CAF50';
ctx.fillRect(0,0,64,16);
for(let i=0; i<200; i++) {
ctx.fillStyle = Math.random() > 0.5 ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.1)';
ctx.fillRect(Math.random()*64, Math.random()*64, 2, 2);
}
ctx.strokeStyle = 'rgba(0,0,0,0.2)';
ctx.strokeRect(0,0,64,64);
const tex = new THREE.CanvasTexture(canvas);
tex.magFilter = THREE.NearestFilter;
return tex;
}
function generateWorld() {
for(let x=0; x<WORLD_SIZE; x++) {
for(let z=0; z<WORLD_SIZE; z++) {
const h = Math.floor(
(Math.sin(x/4) + Math.cos(z/5)) * 2 +
(Math.sin(x/10) + Math.cos(z/10)) * 2 +
WORLD_HEIGHT/2
);
for(let y=0; y<=h; y++) {
setVoxel(x, y, z, 1);
}
}
}
}
function setVoxel(x, y, z, type) {
const key = `${x},${y},${z}`;
if (type === 0) {
delete voxels[key];
} else {
voxels[key] = type;
}
}
function getVoxel(x, y, z) {
return voxels[`${x},${y},${z}`] ? 1 : 0;
}
function rebuildMesh() {
let i = 0;
for (let key in voxels) {
const [x, y, z] = key.split(',').map(Number);
dummyMatrix.position.set(x, y, z);
dummyMatrix.updateMatrix();
instancedMesh.setMatrixAt(i, dummyMatrix.matrix);
i++;
}
for(let j=i; j<maxInstances; j++) {
dummyMatrix.position.set(0, -1000, 0);
dummyMatrix.updateMatrix();
instancedMesh.setMatrixAt(j, dummyMatrix.matrix);
}
instancedMesh.count = maxInstances;
instancedMesh.instanceMatrix.needsUpdate = true;
instanceCount = i;
document.getElementById('debug').innerText = `Blocks: ${instanceCount} | Pos: ${Math.floor(player.position.x)},${Math.floor(player.position.y)},${Math.floor(player.position.z)}`;
}
function setupTouchControls() {
const stickContainer = document.getElementById('stick-container');
const stickNub = document.getElementById('stick-nub');
const btnJump = document.getElementById('btn-jump');
const btnMode = document.getElementById('btn-mode');
stickContainer.addEventListener('touchstart', (e) => {
e.preventDefault();
const touch = e.changedTouches[0];
touches.moveId = touch.identifier;
touches.startX = touch.clientX;
touches.startY = touch.clientY;
updateJoystick(touch.clientX, touch.clientY);
}, {passive: false});
stickContainer.addEventListener('touchmove', (e) => {
e.preventDefault();
for(let i=0; i<e.changedTouches.length; i++) {
if(e.changedTouches[i].identifier === touches.moveId) {
const t = e.changedTouches[i];
updateJoystick(t.clientX, t.clientY);
}
}
}, {passive: false});
const endJoystick = (e) => {
for(let i=0; i<e.changedTouches.length; i++) {
if(e.changedTouches[i].identifier === touches.moveId) {
touches.moveId = null;
input.moveX = 0;
input.moveY = 0;
stickNub.style.transform = `translate(-50%, -50%)`;
}
}
};
stickContainer.addEventListener('touchend', endJoystick);
stickContainer.addEventListener('touchcancel', endJoystick);
function updateJoystick(cx, cy) {
const rect = stickContainer.getBoundingClientRect();
const centerX = rect.left + rect.width/2;
const centerY = rect.top + rect.height/2;
let dx = cx - centerX;
let dy = cy - centerY;
const maxDist = rect.width/2;
const dist = Math.sqrt(dx*dx + dy*dy);
if(dist > maxDist) {
dx = (dx/dist) * maxDist;
dy = (dy/dist) * maxDist;
}
stickNub.style.transform = `translate(calc(-50% + ${dx}px), calc(-50% + ${dy}px))`;
input.moveX = dx / maxDist;
input.moveY = dy / maxDist;
}
document.addEventListener('touchstart', (e) => {
if(e.target.closest('.control-area')) return;
for(let i=0; i<e.changedTouches.length; i++) {
const t = e.changedTouches[i];
if(t.identifier !== touches.moveId && touches.lookId === null) {
touches.lookId = t.identifier;
touches.lastLookX = t.clientX;
touches.lastLookY = t.clientY;
touches.tapStartTime = Date.now();
touches.isTap = true;
}
}
}, {passive: false});
document.addEventListener('touchmove', (e) => {
for(let i=0; i<e.changedTouches.length; i++) {
const t = e.changedTouches[i];
if(t.identifier === touches.lookId) {
const dx = t.clientX - touches.lastLookX;
const dy = t.clientY - touches.lastLookY;
const sens = 0.005;
camera.rotation.y -= dx * sens;
camera.rotation.x -= dy * sens;
camera.rotation.x = Math.max(-Math.PI/2, Math.min(Math.PI/2, camera.rotation.x));
touches.lastLookX = t.clientX;
touches.lastLookY = t.clientY;
if(Math.abs(dx) > 2 || Math.abs(dy) > 2) touches.isTap = false;
}
}
}, {passive: false});
document.addEventListener('touchend', (e) => {
for(let i=0; i<e.changedTouches.length; i++) {
const t = e.changedTouches[i];
if(t.identifier === touches.lookId) {
if(touches.isTap && (Date.now() - touches.tapStartTime) < 300) {
performAction();
}
touches.lookId = null;
}
}
});
btnJump.addEventListener('touchstart', (e) => { e.preventDefault(); input.jump = true; });
btnJump.addEventListener('touchend', (e) => { e.preventDefault(); input.jump = false; });
btnMode.addEventListener('touchstart', (e) => {
e.preventDefault();
input.placeMode = !input.placeMode;
if(input.placeMode) {
btnMode.innerText = "Mode: Place";
btnMode.classList.add('active-mode');
} else {
btnMode.innerText = "Mode: Break";
btnMode.classList.remove('active-mode');
}
});
}
function performAction() {
raycaster.setFromCamera(new THREE.Vector2(0,0), camera);
const intersects = raycaster.intersectObject(instancedMesh);
if (intersects.length > 0) {
const hit = intersects[0];
if (!input.placeMode) {
const p = hit.point.clone().addScaledVector(raycaster.ray.direction, 0.1);
const x = Math.floor(p.x);
const y = Math.floor(p.y);
const z = Math.floor(p.z);
if (y > 0) {
setVoxel(x, y, z, 0);
rebuildMesh();
}
} else {
const p = hit.point.clone().add(hit.face.normal.multiplyScalar(0.1));
const x = Math.floor(p.x);
const y = Math.floor(p.y);
const z = Math.floor(p.z);
const playerBox = new THREE.Box3(
new THREE.Vector3(player.position.x - 0.3, player.position.y - 1.6, player.position.z - 0.3),
new THREE.Vector3(player.position.x + 0.3, player.position.y, player.position.z + 0.3)
);
const blockBox = new THREE.Box3(
new THREE.Vector3(x, y, z),
new THREE.Vector3(x+1, y+1, z+1)
);
if (!playerBox.intersectsBox(blockBox)) {
setVoxel(x, y, z, 1);
rebuildMesh();
}
}
}
}
function updatePhysics(dt) {
if(dt > 0.1) dt = 0.1;
const forward = new THREE.Vector3(0, 0, -1).applyQuaternion(camera.quaternion);
forward.y = 0; forward.normalize();
const right = new THREE.Vector3(1, 0, 0).applyQuaternion(camera.quaternion);
right.y = 0; right.normalize();
const moveVec = new THREE.Vector3();
moveVec.addScaledVector(forward, -input.moveY);
moveVec.addScaledVector(right, input.moveX);
if(moveVec.length() > 0) moveVec.normalize();
player.velocity.x = moveVec.x * player.speed;
player.velocity.z = moveVec.z * player.speed;
if (input.jump && player.grounded) {
player.velocity.y = player.jumpForce;
player.grounded = false;
}
player.velocity.y -= player.gravity * dt;
player.position.x += player.velocity.x * dt;
if(checkCollision(player.position)) {
player.position.x -= player.velocity.x * dt;
player.velocity.x = 0;
}
player.position.z += player.velocity.z * dt;
if(checkCollision(player.position)) {
player.position.z -= player.velocity.z * dt;
player.velocity.z = 0;
}
player.position.y += player.velocity.y * dt;
if(checkCollision(player.position)) {
player.position.y -= player.velocity.y * dt;
if(player.velocity.y < 0) {
player.grounded = true;
player.velocity.y = 0;
player.position.y = Math.round(player.position.y * 100) / 100;
} else {
player.velocity.y = 0;
}
} else {
player.grounded = false;
}
if(player.position.y < -10) {
player.position.set(WORLD_SIZE/2, WORLD_HEIGHT + 5, WORLD_SIZE/2);
player.velocity.set(0,0,0);
}
camera.position.copy(player.position);
}
function checkCollision(pos) {
const r = player.radius;
const h = player.height;
const x1 = Math.floor(pos.x - r);
const x2 = Math.floor(pos.x + r);
const z1 = Math.floor(pos.z - r);
const z2 = Math.floor(pos.z + r);
const y1 = Math.floor(pos.y - h);
const y2 = Math.floor(pos.y - 0.1);
for(let x = x1; x <= x2; x++) {
for(let z = z1; z <= z2; z++) {
for(let y = y1; y <= y2; y++) {
if(getVoxel(x, y, z)) return true;
}
}
}
return false;
}
function animate() {
requestAnimationFrame(animate);
const time = performance.now();
const dt = (time - lastTime) / 1000;
lastTime = time;
updatePhysics(dt);
renderer.render(scene, camera);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment