Using HTML5 Canvas to make awesome life Simulation with a very nice performance (atleast in Chromium)
-
-
Save b1nary/3067450 to your computer and use it in GitHub Desktop.
Game of Life / Copyworld / Langton's Ant Simulation in HTML5 Canvas Element
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<title>GameOfLife Html5 Canvas</title> | |
<style> | |
canvas { border:2px solid #555; box-shadow:0px 0px 4px #999; } | |
body { background-color:#666; font-family:Monospace; color:white; } | |
table { width: 800px; } | |
input[type=range] { width:680px; } | |
</style> | |
<script> | |
var map = null; | |
var xmap = null; | |
var runner = null; | |
var speed = null; | |
var mousePress = false; | |
var last = new Array(4); | |
var count = 0; | |
// Easily create 2d arrays | |
function Array2D(x,y) { | |
var out = new Array(x); | |
for (var i = 0; i < x; i++) { | |
out[i] = new Array(y); | |
} | |
return out; | |
} | |
// Draw Function gets called "always" | |
function draw() { | |
ctx.fillStyle="#333"; | |
ctx.fillRect(0, 0, c.width, c.height); | |
ctx.fillStyle="#555"; | |
ctx.shadowBlur = 5; | |
ctx.shadowColor = "#222"; | |
// Recreating map | |
if (running == true) { | |
count = count += 1; | |
// Conways Orginal (23/3) | |
if (rules == "23-3"){ | |
for (yy=0; yy<box_height; yy++){ | |
for (xx=0; xx<box_width; xx++){ | |
var found = 0; | |
// Count alive's around, kind of ugly ... | |
if (yy > 0 && xx > 0 && map[yy-1][xx-1] == true) { found = found+1; } | |
if (yy > 0 && map[yy-1][xx] == true) { found = found+1; } | |
if (yy > 0 && xx < box_height-1 && map[yy-1][xx+1] == true) { found = found+1; } | |
if (xx > 0 && map[yy][xx-1] == true) { found = found+1; } | |
if (xx < box_width-1 && map[yy][xx+1] == true) { found = found+1; } | |
if (yy < box_height-1 && xx > 0 && map[yy+1][xx-1] == true) { found = found+1; } | |
if (yy < box_height-1 && map[yy+1][xx] == true) { found = found+1; } | |
if (yy < box_height-1 && xx < box_width-1 && map[yy+1][xx+1] == true) { found = found+1; } | |
if (map[yy][xx] == true) { | |
if (found < 2){ mapx[yy][xx] = false; } | |
if (found > 3){ mapx[yy][xx] = false; } | |
} else { | |
if (found == 3){ mapx[yy][xx] = true; } | |
} | |
} | |
} | |
}// Copyworld (1357/1357) | |
else if (rules == "1357-1357") { | |
for (yy=0; yy<box_height; yy++){ | |
for (xx=0; xx<box_width; xx++){ | |
var found = 0; | |
if (yy > 0 && xx > 0 && map[yy-1][xx-1] == true) { found = found+1; } | |
if (yy > 0 && map[yy-1][xx] == true) { found = found+1; } | |
if (yy > 0 && xx < box_height-1 && map[yy-1][xx+1] == true) { found = found+1; } | |
if (xx > 0 && map[yy][xx-1] == true) { found = found+1; } | |
if (xx < box_width-1 && map[yy][xx+1] == true) { found = found+1; } | |
if (yy < box_height-1 && xx > 0 && map[yy+1][xx-1] == true) { found = found+1; } | |
if (yy < box_height-1 && map[yy+1][xx] == true) { found = found+1; } | |
if (yy < box_height-1 && xx < box_width-1 && map[yy+1][xx+1] == true) { found = found+1; } | |
if (found == 0 || found == 2 || found == 4 || found == 6 || found == 8){ mapx[yy][xx] = false; } | |
if (found == 1 || found == 3 || found == 5 || found == 7 ){ mapx[yy][xx] = true; } | |
} | |
} | |
}// Langton's ant | |
else if (rules = "ant") { | |
if (last[1] == null || last[1] == undefined){ | |
mapx[Math.round(box_height/2)][Math.round(box_width/2)] = true; | |
last[0] = Math.round(box_height/2); | |
last[1] = Math.round(box_width/2); | |
} | |
if (last[2] == null || last[2] == 0 || last[2] == undefined){ | |
last[2] = 1; | |
} | |
if (map[last[0]][last[1]] == false){ | |
mapx[last[0]][last[1]] = true; | |
last[2] = last[2] + 1; | |
if(last[2] == 5){ last[2] = 1; } | |
} else { | |
mapx[last[0]][last[1]] = false; | |
last[2] = last[2] - 1; | |
if(last[2] == 0){ last[2] = 4; } | |
} | |
// up | |
if(last[2] == 1){ last[0] = last[0] - 1; if(last[0] < 0){ last[0] = 0; } } | |
// right | |
if(last[2] == 2){ last[1] = last[1] + 1; if(last[1] > c.width-1){ last[1] = c.width-1; } } | |
// down | |
if(last[2] == 3){ last[0] = last[0] + 1; if(last[0] > c.height-1){ last[0] = c.height-1; } } | |
// left | |
if(last[2] == 4){ last[1] = last[1] - 1; if(last[1] < 0){ last[1] = 0; } } | |
} | |
map = mapx; | |
document.getElementById('_count').innerHTML = count+""; | |
} | |
// Print new Map and create backupmap for remapping (above) | |
mapx = Array2D(box_height,box_width); | |
for (yy=0; yy<box_height; yy++){ | |
for (xx=0; xx<box_width; xx++){ | |
if (map[yy][xx] === true) { | |
ctx.fillStyle = color; | |
mapx[yy][xx] = true; | |
} else { | |
ctx.fillStyle = "#555"; | |
mapx[yy][xx] = false; | |
} | |
ctx.fillRect(xx*(box_size)+1, yy*(box_size)+1, box_size-2, box_size-2); | |
} | |
} | |
} | |
// Draw on Canvas with mouse | |
function canvas_draw(ev) { | |
if(mousePress){ | |
var x = Math.round( (ev.clientX - (box_size/2) - c.offsetLeft) / box_size); | |
var y = Math.round( (ev.clientY - (box_size/2) - c.offsetTop) / box_size); | |
if (y != last[0] || x != last[1]){ | |
if(map[y][x] == true){ | |
last[0] = y; last[1] = x; | |
map[y][x] = false; | |
} else { | |
last[0] = y; last[1] = x; | |
map[y][x] = true; | |
} | |
if(running == false){ | |
draw(); | |
} | |
} | |
} | |
} | |
// Init function, called from body load | |
function init() { | |
box_size = 10; | |
box_width = 80; | |
box_height = 60; | |
speed = 240; | |
rules = "23-3"; // Conway | |
c = document.getElementById("canvas"); | |
ctx = c.getContext("2d"); | |
map = Array2D(box_height,box_width); | |
mapx = Array2D(box_height,box_width); | |
running = false; | |
color = "#5f5"; | |
var mousePress = true; | |
c.addEventListener('mousedown', mouseDown, false); | |
c.addEventListener('mousemove', canvas_draw, false); | |
c.addEventListener('mouseup', mouseUp, false); | |
draw(); | |
} | |
function mouseDown(ev){ | |
mousePress = true; | |
canvas_draw(); | |
} | |
function mouseUp(ev){ | |
mousePress = false; | |
} | |
// Run function, Toggle run button | |
function run() { | |
running = true; | |
runner = setInterval(draw, speed); | |
} | |
// Stop function, Toggle run button | |
function stop() { | |
running = false; | |
clearInterval(runner); | |
var btn = document.getElementById("_run"); | |
btn.value = "Run"; | |
} | |
// Toggle run button | |
function runtoggle() { | |
var btn = document.getElementById("_run"); | |
if(btn.value == "Run") { | |
btn.value = "Stop"; | |
run(); | |
} else { | |
stop(); | |
} | |
} | |
// Color Change function | |
function set_color() { | |
var mylist = document.getElementById("_color"); | |
var val = mylist.options[mylist.selectedIndex].value; | |
color = val; | |
draw(); | |
} | |
// Speed changed function | |
function set_speed() { | |
speed = document.getElementById("_speed").value; | |
document.getElementById("_speed_label").innerHTML = speed+""; | |
if(running == true){ | |
stop(); | |
run(); | |
} | |
} | |
// Size changed function | |
function set_size() { | |
size = document.getElementById("_size").value.split(":"); | |
box_size = parseInt(size[0]); | |
box_width = parseInt(size[1]); | |
box_height = parseInt(size[2]); | |
map = Array2D(box_height,box_width); | |
mapx = Array2D(box_height,box_width); | |
draw(); | |
} | |
// Rules changed function | |
function set_rules() { | |
rules = document.getElementById("_rules").value; | |
} | |
// Fill map random function | |
function random_map() { | |
var cc = Math.round(Math.random()*(box_height*box_width)); | |
for (var i = 0; i < cc; i++) { | |
try { map[ Math.round( Math.random() * box_height - 1 ) ][ Math.round( Math.random() * box_width - 1 ) ] = true; } catch(e) {} | |
} | |
draw(); | |
} | |
// Clear Map function | |
function clear_map() { | |
last = new Array(4); | |
map = Array2D(box_height,box_width); | |
if(running == true){ | |
stop(); | |
} | |
draw(); | |
count = 0; | |
} | |
// Next ;) | |
function next() { | |
running = true; | |
draw(); | |
running = false; | |
} | |
</script> | |
<body onload="init();"> | |
<center> | |
<table border="0"> | |
<tr> | |
<td><h1>Game Of Life</h1></td> | |
<td> </td> | |
<td> | |
<input id="_next" type="button" value="Next" onclick="next();"> | |
<input id="_run" type="button" value="Run" style="width:40px;" onclick="runtoggle();"> | |
<input id="_randmap" type="button" value="Random" onclick="random_map();"> | |
<input id="_clearmap" type="button" value="Clear" onclick="clear_map();"> | |
<select id="_color" onchange="set_color();"> | |
<option value="#5f5">Green</option> | |
<option value="#f55">Red</option> | |
<option value="#ff5">Yellow</option> | |
<option value="#55f">Blue</option> | |
<option value="#fff">White</option> | |
</select> | |
<select id="_size" onchange="set_size();"> | |
<option value="40:20:15">Huge (20x15)</option> | |
<option value="20:40:30">Big (40x30)</option> | |
<option value="10:80:60" selected>Nice (80x60)</option> | |
<option value="5:160:120">Small (160x120)</option> | |
<option value="3:320:240">Mini (320x240)</option> | |
</select> | |
<select id="_rules" onchange="set_rules();"> | |
<option value="23-3">Conway's Orginal (23/3)</option> | |
<option value="1357-1357">Copy World (1357/1357)</option> | |
<option value="ant">Langton's ant</option> | |
</select> | |
<span id="_count">0</span> | |
</td> | |
</tr> | |
</table> | |
<canvas id="canvas" width="800" height="600"> | |
This text is displayed if your browser does not support HTML5 Canvas. | |
</canvas> | |
<br> | |
<table border="0"> | |
<tr> | |
<td><big><strong>Speed </strong></big></td> | |
<td><input type="range" title="Delay in Milliseconds" width="800" min="2" id="_speed" max="800" value="240" step="5" onchange="set_speed();" /></td> | |
<td><big id="_speed_label">240</big></td> | |
</tr> | |
</table> | |
</center> | |
</body> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment