oneshotlm
Compare all models on this prompt →

Rubik's Cube · gemini-3.1-flash-lite-preview

Using the pre-provided three.min.js (global THREE), render an interactive 3D Rubik's Cube with clickable face rotations, orbit-drag camera, and a scramble button.

⛨ sandboxed Open in new tab ↗
transcript · 20 messages
user

Using the pre-provided three.min.js (global THREE), render an interactive 3D Rubik's Cube with clickable face rotations, orbit-drag camera, and a scramble button.

assistant
thinking
**Developing the Core Structure**

I'm focusing on the fundamental structure, aiming for a singular `index.html` implementation. I've begun integrating `three.min.js` and have initiated the construction of the interactive 3D Rubik's Cube within the HTML file. My current task revolves around implementing clickable face rotations. I have the basic three.js scene set up, and I will be working on the core geometry, camera, and basic cube.


bash {"command":"ls -l three.min.js"}
toolResult

-rw-r--r-- 1 node node 603445 Jul 22 17:59 three.min.js

assistant
thinking
**Constructing Basic Structure**

I have started building the project's foundation. The initial step, `three.min.js`, is present. I'm now crafting `index.html`, where the main structure will be outlined. I'll begin with the essential HTML framework before initializing the THREE.js scene and creating the cubes. Following that, I will implement rotation logic, and add UI functionality, including a scramble button, and mouse control.


write index.html (3258 bytes)
toolResult

Successfully wrote 3258 bytes to index.html

assistant
thinking
**Developing Cube Structure**

Okay, I've got a basic monochrome block implemented – that’s the starting point. The core of my current focus is now the visuals. I need to make the cube look like a Rubik's Cube; the prompt calls for an interactive 3D model, specifically with clickable face rotations. I have the orbit-drag function done for the most part, though. I'll need a scramble button too.


edit {"path":"index.html","edits":[{"oldText":" // Build Cube\n function createCube() {\n const material = new THREE.Mes
toolResult

Successfully replaced 1 block(s) in index.html.

assistant
thinking
**Focusing on Click Rotation**

I've colored the cubes, and now I'm grappling with the "Clickable face rotation" functionality. It looks like implementing the full group-rotation logic is going to get too complex, so I'm considering using `THREE.Raycaster` for mesh detection. If a mesh is clicked, I will...


edit {"path":"index.html","edits":[{"newText":" // Interaction (Basic Orbit & Rotation)\n const raycaster = new THREE.Raycaster();\
toolResult

Successfully replaced 1 block(s) in index.html.

assistant
thinking
**Considering the Rotation**

I've realized that a basic "click and rotate" feature is the simplest path forward. Implementing the proper face rotation, however, presents some logical complexities. I think I'll initially go for the simpler approach, and just set up a rotation on click. This might lead to an orbit style, using the scene rotation.


edit {"edits":[{"oldText":" document.addEventListener('click', (e) => {\n mouse.x = (e.clientX / window.innerWidth) * 2 - 1;\n
toolResult

Successfully replaced 1 block(s) in index.html.

assistant
thinking
**Analyzing Rotation Issues**

I'm currently stuck on a rotation animation bug. I'm concerned that `scene.attach` might be problematic for the object hierarchy. My current loop condition, `current < targetRotation`, seems sound, but I suspect it may struggle with coordinate "rounding". This could lead to misalignment if the rotations don't grid perfectly, breaking the animation.


edit {"path":"index.html","edits":[{"newText":" // Animate\n const targetRotation = Math.PI / 2;\n let current =
toolResult

Successfully replaced 1 block(s) in index.html.

assistant
edit {"path":"index.html","edits":[{"newText":" // Scramble\n document.getElementById('scramble').onclick = () => {\n fo
toolResult

Successfully replaced 1 block(s) in index.html.

assistant
bash {"command":"# No changes needed, final check of structure\nls -l index.html three.min.js"}
toolResult

-rw-r--r-- 1 node node 7627 Jul 22 17:59 index.html -rw-r--r-- 1 node node 603445 Jul 22 17:59 three.min.js

assistant
thinking
**Considering Post-Rotation Updates**

I'm now zeroing in on a potential flaw. I'm wondering if the `cubies` array, specifically the position data for each cube, is updated *after* a rotation occurs. Currently, slice determination relies on the cube's initial `c.position`. If this isn't updated, rotations could become inaccurate. I think my `animateRotation` already handles this update but I need to make sure.


edit {"path":"index.html","edits":[{"newText":" // Build Cube\n function createCube() {\n // Standard Rubik's Colors\n
toolResult

Successfully replaced 1 block(s) in index.html.

assistant

This single-file Rubik's Cube implementation uses the provided `three.min.js`. The app features a 3x3x3 grid, interactive face rotations through raycasting and grouping, right-click orbit controls, and a scramble button. ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>3D Rubik's Cube</title> <style> body { margin: 0; overflow: hidden; background: #222; font-family: sans-serif; } #ui { position: absolute; top: 10px; left: 10px; display: flex; gap: 10px; } button { padding: 10px 20px; font-size: 16px; cursor: pointer; border: none; border-radius: 5px; background: #444; color: white; } button:hover { background: #666; } </style> </head> <body> <div id="ui"> <button id="scramble">Scramble</button> </div> <script src="three.min.js"></script> <script> const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); camera.position.set(5, 5, 5); camera.lookAt(0, 0, 0); const light = new THREE.DirectionalLight(0xffffff, 1); light.position.set(5, 5, 5); scene.add(light); scene.add(new THREE.AmbientLight(0x404040)); const CUBE_SIZE = 1; const GAP = 0.05; const cubies = []; function createCube() { const colors = { px: 0xff0000, nx: 0xffa500, py: 0xffffff, ny: 0xffff00, pz: 0x0000ff, nz: 0x00ff00 }; for (let x = -1; x <= 1; x++) { for (let y = -1; y <= 1; y++) { for (let z = -1; z <= 1; z++) { const materials = [ new THREE.MeshLambertMaterial({ color: colors.px }), new THREE.MeshLambertMaterial({ color: colors.nx }), new THREE.MeshLambertMaterial({ color: colors.py }), new THREE.MeshLambertMaterial({ color: colors.ny }), new THREE.MeshLambertMaterial({ color: colors.pz }), new THREE.MeshLambertMaterial({ color: colors.nz }), ]; const geometry = new THREE.BoxGeometry(CUBE_SIZE - GAP, CUBE_SIZE - GAP, CUBE_SIZE - GAP); const box = new THREE.Mesh(geometry, materials); box.position.set(x * CUBE_SIZE, y * CUBE_SIZE, z * CUBE_SIZE); scene.add(box); cubies.push(box); } } } } createCube(); const raycaster = new THREE.Raycaster(); const mouse = new THREE.Vector2(); function rotateLayer(normal, position) { const group = new THREE.Group(); const threshold = 0.5; let axis = new THREE.Vector3(); if (Math.abs(normal.x) > 0.5) axis.set(1, 0, 0); else if (Math.abs(normal.y) > 0.5) axis.set(0, 1, 0); else axis.set(0, 0, 1); const layer = cubies.filter(c => { const pos = c.position.clone(); if (axis.x && Math.abs(pos.x - position.x) < threshold) return true; if (axis.y && Math.abs(pos.y - position.y) < threshold) return true; if (axis.z && Math.abs(pos.z - position.z) < threshold) return true; return false; }); scene.add(group); layer.forEach(c => { group.attach(c); }); const targetRotation = Math.PI / 2; let current = 0; const step = 0.15; function animate() { if (current < targetRotation) { const s = Math.min(step, targetRotation - current); if (axis.x) group.rotation.x += s; else if (axis.y) group.rotation.y += s; else group.rotation.z += s; current += s; requestAnimationFrame(animate); } else { layer.forEach(c => { const wPos = new THREE.Vector3(); const wQuat = new THREE.Quaternion(); c.getWorldPosition(wPos); c.getWorldQuaternion(wQuat); scene.attach(c); c.position.set(Math.round(wPos.x), Math.round(wPos.y), Math.round(wPos.z)); c.quaternion.copy(wQuat); }); scene.remove(group); } } animate(); } document.addEventListener('click', (e) => { mouse.x = (e.clientX / window.innerWidth) * 2 - 1; mouse.y = -(e.clientY / window.innerHeight) * 2 + 1; raycaster.setFromCamera(mouse, camera); const intersects = raycaster.intersectObjects(cubies); if (intersects.length > 0) rotateLayer(intersects[0].face.normal, intersects[0].object.position); }); let isDragging = false; let pM = { x: 0, y: 0 }; document.addEventListener('mousedown', (e) => { if(e.button === 2) isDragging = true; }); document.addEventListener('mouseup', () => isDragging = false); window.oncontextmenu = () => false; document.addEventListener('mousemove', (e) => { if (isDragging) { scene.rotation.y += (e.clientX - pM.x) * 0.01; scene.rotation.x += (e.clientY - pM.y) * 0.01; } pM = { x: e.clientX, y: e.clientY }; }); document.getElementById('scramble').onclick = () => { for(let i=0; i<10; i++) { const c = cubies[Math.floor(Math.random() * cubies.length)]; const n = [new THREE.Vector3(1,0,0), new THREE.Vector3(0,1,0), new THREE.Vector3(0,0,1)][Math.floor(Math.random()*3)]; rotateLayer(n, c.position); } }; function animate() { requestAnimationFrame(animate); renderer.render(scene, camera); } animate(); </script> </body> </html> ```

TAKE IT TO CODE

A single self-contained HTML file — copy it and open in any browser, or reuse the prompt with your own pi-agent run.

RUN

modelgemini-3.1-flash-lite-preview
vendorGoogle
harnesspi-agent
generated2026-07-22

EVALUATION

3/5 partial

Rubik's cube renders correctly; click triggered a face rotation/reveal, but drag/wheel/keys show no camera or rotation response.

Vision grade · anthropic/claude-sonnet-5

MEASUREMENTS

Raw readings from the capture, not a verdict — the grade above is the only judgement. Percentages are how much of the screen changed; the measure under-reports sparse motion on a dark background.

idle motion0.0%
on click2.8%
on drag0.0%
on wheel0.0%
on enter + space0.0%
on arrow keys0.0%
on w/a/s/d0.0%
frame spread33.5 / 255
console errors0
js errors none

METRICS

runtime31.8s
tokens in51k
tokens out8.1k
cached16k
cost / run$0.0213