Click the screenshot to launch the demo. You will need to use the latest version of Chrome, Firefox or Safari if you want to see it running.
This is a re-creation of a Flash experiment from several years ago. Back before I started playing around with 3d, I created a series of experiments exploring cellular automata, the majority of which were variations on the Langton’s Ant algorithm. The most advanced version was one in pseudo-3d, and that was the last for over three years. Now with the advent of easy-to-use 3d libraries (like Three.js, used here), and the option of hardware acceleration to allow for complex animations, I felt it was time to re-visit some of the old experiments.
Here is the source code for the 3d ant:
window.requestAnimFrame = (function(){ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function( callback ){ window.setTimeout(callback, 1000 / 60); }; })(); var canvas, stage, container, scene, camera, renderer, projector, light; var WIDTH = 640, HEIGHT = 640; VIEW_ANGLE = 45, ASPECT = WIDTH/HEIGHT, NEAR = .1, FAR = 10000, centerX = 0, centerY = 0, centerZ = 0, cameraX = 0, cameraY = 200, cameraZ = 400; var antX = 32, antY = 32, antZ = 32, nextX, nextY, nextZ, cellsX = 64, cellsY = 64, cellsZ = 64, cellWidth = 8, cellHeight = 8, cellDepth = 8, antSize = 7, maxDirections = 8, colorMultiplier = Math.round(256/cellsX), xOff = cellsX/2*cellWidth, yOff = cellsY/2*cellHeight, zOff = cellsZ/2*cellDepth, base, objects, antDirection = 1, filledCells=0, isRotating=true, isPaused=false; function init() { canvas = document.getElementById("myCanvas"); scene = new THREE.Scene(); camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR ); camera.position.x = cameraX; camera.position.y = cameraY; camera.position.z = cameraZ; camera.lookAt(scene.position); scene.add( camera ); projector = new THREE.Projector(); renderer = new THREE.WebGLRenderer( { antialias: true } ); renderer.setSize( WIDTH, HEIGHT ); light = new THREE.SpotLight(); light.position.set( cameraX, cameraY, cameraZ ); scene.add(light); canvas.appendChild( renderer.domElement ); renderer.render(scene,camera); base = new THREE.Object3D(); base.position.x = centerX; base.position.y = centerY; base.position.x = centerZ; base.rotation.y = 45; scene.add(base); camera.lookAt(base.position); objects = new Array(cellsX); for(var i=0;i<objects.length;i++) { objects[i] = new Array(cellsY); for(var j=0;j<objects[i].length;j++) { objects[i][j] = new Array(cellsZ); } } onEnterFrame(); } function onEnterFrame() { requestAnimFrame(onEnterFrame); renderer.render(scene,camera); if(isPaused==true) return; if(!objects[antX][antY][antZ]) { antDirection++; if(antDirection == maxDirections) antDirection = 0; addObject(antX,antY,antZ); } else { removeObject(antX,antY,antZ); antDirection--; if(antDirection == -1) antDirection = maxDirections-1; } switch(antDirection) { case 0: antZ--; break; case 1: antX++; break; case 2: antY++; break; case 3: antX--; break; case 4: antZ++; break; case 5: antX++; break; case 6: antY--; break; case 7: antX--; break; default: break; } if(antY < 0) antY += cellsY; if(antY >= cellsY) antY -= cellsY; if(antX < 0) antX += cellsX; if(antX >= cellsX) antX -= cellsX; if(antZ < 0) antZ += cellsZ; if(antZ >= cellsZ) antZ -= cellsZ; if(isRotating==true) base.rotation.y +=.01; document.getElementById("count").value = filledCells; } function addObject(x,y,z) { var sphereMaterial = new THREE.MeshPhongMaterial({ color: getRGB(x,y,z), opacity:1 }); var obj = new THREE.Mesh( new THREE.CubeGeometry(antSize,antSize,antSize), sphereMaterial); obj.position.x = x*cellWidth-xOff; obj.position.y = y*cellHeight-yOff; obj.position.z = z*cellDepth-zOff; obj.overdraw = true; base.add(obj); objects[x][y][z] = obj; filledCells++; } function removeObject(x,y,z) { base.remove(objects[x][y][z]); objects[x][y][z] = null; filledCells--; } function getRGB(r,g,b) { var rgb = parseInt((r*colorMultiplier).toString(16) + (g*colorMultiplier).toString(16) + (b*colorMultiplier).toString(16),16); return rgb; } onload=init;
The above code assumes you have downloaded a copy of Three.js and have a HTML page with appropriate elements set up. To see the entirety of the code I used, right-click on the screenshot and open it in a new tab or window, then view source. It’s all one file, other than the Three.js library. Feel free to copy it in its entirety and play around.