Brownian Motion in HTML5 Canvas
Ya know, cause I couldn’t find it anywhere (Lovingly stolen from Processing documentation). View the example.
I’m hoping to convert Craig Reynolds’ Boids to JavaScript/Canvas at some point in the future, too.
window.addEventListener('load', init, false);
function init(){
var canvas = document.getElementById("canvas");
if (canvas.getContext('2d')){
main(canvas);
} else {
console.log("Canvas tag is not supported");
}
}
function main(canvas){
var canvas = canvas;
var ctx = canvas.getContext("2d");
const CANVAS_WIDTH = canvas.width;
const CANVAS_HEIGHT = canvas.height;
var num = 10;
var range = 200;
var ax = [];
var ay = [];
for(var i = 0; i < num; i++) {
ax[i] = CANVAS_WIDTH/2;
ay[i] = CANVAS_HEIGHT/2;
}
setInterval(draw, 30);
function draw(){
// Shift all elements 1 place to the left
for(var i = 1; i < num; i++) {
ax[i-1] = ax[i];
ay[i-1] = ay[i];
}
// Put a new value at the end of the array
var r2x = range*2;
ax[num-1] += (Math.random()*r2x)-range;
ay[num-1] += (Math.random()*r2x)-range;
// Constrain all povars to the screen
ax[num-1] = constrain(ax[num-1], 0, CANVAS_WIDTH);
ay[num-1] = constrain(ay[num-1], 0, CANVAS_HEIGHT);
// Draw a line connecting the povars
for(var i = 1; i < num; i++) {
var alpha = i/num;
ctx.globalAlpha = i/num;
ctx.moveTo(ax[i-1], ay[i-1]);
ctx.lineTo(ax[i], ay[i]);
ctx.stroke();
}
}
function constrain(val, min, max){
if(val > max){
val = max;
}else if(val < min){
val = min;
}
return val;
}
}
Working with HTML5 Range Input
I felt compelled to post this HTML5 range input example because I couldn’t find code online that didn’t rely on JQuery to output a value (not that I have anything against it, just that it can be overkill for simple things like this).
The code below outputs the value of the range to a nearby <span>. I created a “showOutput” function to make it easier to use with multiple inputs.
window.addEventListener('load', initSlider, false);
/**
* Initializes sliders
*/
function initSlider(){
// get range input
var myRange = document.getElementById("myRange");
myRange.addEventListener('change', function(){showOutput(myRange, "rangeOuput")}, false);
document.getElementById("rangeOuput").innerHTML = rangeOuput.value;
// call range input on window init to display initial value
showOutput(myRange, "rangeOuput");
/**
* Displays output to text fields.
* @param slider id of range input
* @param field id of span to write value.
*/
function showOutput(slider, field){
document.getElementById(field).innerHTML = slider.value;
}
}
Revisiting Interactive Media
I recently read a CNN op-ed piece by my former instructor, Doug Rushkoff, on how coding should be taught in every school as a way to be more competitive in the global industry. I couldn’t agree more.
For the past two years I’ve taught an Interactive Media course at Harrisburg Area Community College that has traditionally been taught in Adobe Flash. This semester, I decided to revisit the medium in which to teach interactive media, especially now that web-based Flash is being replaced with web standards technology like HTML, CSS, and JavaScript.
I haven’t completely replaced Flash in the course, as it remains a valuable tool for complex animation, video, and games. I have, however, decided to begin with the fundamentals of programming, and feel that the Flash IDE has too many distractions to serve as the starting medium.
I gave a lot of thought towards Processing, provided it’s easy-to-approach IDE, its large community, and its excellent documentation. In the end, though, I decided on HTML5 Canvas, as my students will likely end up using javascript instead of Java.
To a more selfish end, it’s forced me to better grasp canvas and JavaScript. I purposely chose a medium I wanted to learn, as I’m already comfortable with Flash and doubt I will be revisiting Java.
I’m going to try and start posting more code, such as this for the Fibonacci example:
window.addEventListener('load', init, false);
var canvas;
/**
* Detects for context 2d
*/
function init(){
canvas = document.getElementById("myCanvas");
if (canvas.getContext('2d')){
main(canvas);
initSliders();
} else {
console.log("Canvas not supported");
}
}
/**
* Initializes sliders
*/
function initSliders(){
var radRange = document.getElementById("radRange");
radRange.addEventListener('change', function(){showOutput(radRange, "rangeOuput")}, false);
document.getElementById("rangeOuput").innerHTML = radRange.value;
var distRange = document.getElementById("distRange");
distRange.addEventListener('change', function(){showOutput(distRange, "distOuput")}, false);
document.getElementById("distOuput").innerHTML = distRange.value;
/**
* Displays output to text fields.
* @param slider id of range input
* @param field id of span to write value.
*/
function showOutput(slider, field){
document.getElementById(field).innerHTML = slider.value;
}
}
/**
* Main
* @param canvas target canvas
*/
function main(canvas){
var ctx = canvas.getContext("2d");
var max = 200; // number of items on screen
var i = 0; // initialize for counter
const fib = 1/1.618033989; // Fibonacci Sequence
var radius = 0; // initial radius
var lastInputRadius; // remembers previous state to determine if positioning needs changed
var lastInputDistance; // remembers previous state to determine if positioning needs changed
var circles = []; // Create Array to hold items
const CANVAS_RIGHT = canvas.width;
const CANVAS_BOTTOM = canvas.height;
var interval = setInterval(draw, 30);
/**
* Draws pattern to screen
*/
function draw() {
var inputRadius = document.getElementById("rangeOuput").innerHTML;
var inputDistance = document.getElementById("distOuput").innerHTML;
if(i < max){
// create shape
radius += inputRadius*fib;
lastInputRadius = inputRadius;
lastInputDistance = inputDistance;
var dist = i*inputDistance; //represents distance from object
var x = Math.cos(radius*Math.PI/180)*dist + CANVAS_RIGHT/2;
var y = Math.sin(radius*Math.PI/180)*dist + CANVAS_BOTTOM/2;
var hex = rgbToHex(255-i,0,i);
circles[i] = new Circle(ctx, x, y, (i/max)*100, hex);
i++;
}else{
// if input has changed, reposition shape
if(lastInputRadius != inputRadius || lastInputDistance != inputDistance){
radius += inputRadius*fib;
clear();
for(var j = 0; j < max; j++){
radius += inputRadius*fib;
var dist = j*inputDistance; //represents distance from object
var x = Math.cos(radius*Math.PI/180)*dist + CANVAS_RIGHT/2;
var y = Math.sin(radius*Math.PI/180)*dist + CANVAS_BOTTOM/2;
var hex = rgbToHex(255-j,0,j);
circles[j].render(x, y, (j/max)*100, hex);
}
}
}
lastInputRadius = inputRadius;
lastInputDistance = inputDistance;
}
/**
* Draws circle.
*/
function Circle(ctx, x, y, r, hex){
this.ctx = ctx;
this.centerX = x;
this.centerY = y;
this.radius = r;
this.render = function render(x, y, r, hex){
this.centerX = x;
this.centerY = y;
this.radius = r;
this.ctx.beginPath();
this.ctx.arc(this.centerX, this.centerY, this.radius, 0, 2*Math.PI, false);
this.ctx.fillStyle = hex;
this.ctx.fill();
this.ctx.lineWidth = 2;
this.ctx.strokeStyle = "black";
this.ctx.stroke();
}
this.render(this.centerX, this.centerY, this.radius, hex);
}
/**
* Converts RGB values to hexidecimal
* @param r 0-255 red value
* @param g 0-255 green value
* @param b 0-255 blue value
*/
function rgbToHex(r, g, b) {
/**
* Converts numeric value to base-16
* @param c target value
*/
function componentToHex(c) {
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
/**
* Clears the canvas
*/
function clear(){
ctx.clearRect(0, 0, CANVAS_RIGHT, CANVAS_BOTTOM);
}
}
It’s been difficult not having a definitive API documentation like ActionScript and Processing have. The best I’ve found so far has been HTML5 Canvas Tutorials accompanied by a lot of O’Reilly JavaScript books. Coming from an ActionScript background, I’m still wrapping my head on how every function is an object, and how privatization, and event listening isn’t quite so baked in. I’m know the code above could be optimized more–eliminating globals, calculating x and y-positions once in draw(), etc., but it’s just a sketch.
I haven’t quite figured out how to structure my experiments on this blog, but my goal this year is posting more content, and spending less time examining distractions and vices.
Baseball 2011
I enjoy attending baseball games. It forces me to block out my schedule, not think about work, and spend time with friends.
In 2007 and 2008, I spent a lot of time at Yankee Stadium cheering on the Yankees. It wasn’t difficult, considering I lived near the stadium, had generous season ticket-holding friends in New York, and fell out of following baseball after 1994 strike (it didn’t hurt that my favorite Orioles pitcher–Mussina, changed to pinstripes, either). When I returned to Central PA at the end of 2008, I sort of went through a withdrawal of not attending baseball games. I tried traveling to the Bronx in 2009, but the commute was brutal (sometimes getting home at 4 a.m. or scrambling for a couch in the city) and costly (sometimes paying up to $60 for parking).
Last year, I bought a partial Orioles season ticket plan and rediscovered my roots, so to say (I briefly went to college in Baltimore and grew up an Orioles fan). My first game was a bit surreal, but I can safely say I’ve reverted my allegiance back to my current “home” team. The shorter commute has also proved to be the perfect amount of time to hang out with friends without feeling the exhaustion of a long trip.
I’ve started a tradition with one of my brother-in-laws of visiting a new ballpark each year (this year was PNC in Pittsburgh). I’ve also started a tradition of getting a picture with those I attend baseball games with; I’m posting what I have of 2010 and 2011 (above) online.
I renewed my Orioles seats for 2012, and this year hope to drag a whole new set of people down to the ballpark (possibly including my wife, who isn’t a baseball fan but humors me). If we’re friends and you harbor some secret interest for going to games, you should let me know
Christmas Card 2011
Well, I wrapped up this year’s Christmas card. I actually started with some concepts on paper–before inevitably moving to my iPad and Cintiq. I had another concept I liked better, but that’ll be for next year.
I still prefer sending out my custom-drawn cards, but am getting slight pressure from the Mrs. to do one of those cheesy, glossy postcards with canned messages–just so people can actually see our kids. I suppose I’ll cave in once those photography studios offer cards like this.
In the meanwhile, if there’s any friends of mine that didn’t get one of these cards but want on the list, send me your address.
I’m a sucker for good candy packaging.

I picked up this little guy from Economy Candy the other day. This Bob-omb’s top unscrews to contain a dipper with cherry-flavored sugar, but let’s be honest, I didn’t buy it for the candy.
Biking the Abandoned Turnpike
I feel somewhat guilty for bringing more attention this rather hidden gem, but I found it to be a photographer’s paradise.
If you’re unfamiliar with the abandoned turnpike, it’s a 13-mile stretch of highway off of Breezewood (between Harrisburg and Pittsburgh) that was active from 1940 to 1968. The turnpike commission bypassed this section to alleviate tunnel traffic. As a result, the Sideling Hill and Rays Hill tunnels were abandoned, and a travel plaza was leveled.
The property was sold in 2001 to the Southern Alleghenies Conservancy, and has since been passed along to the Friends of Pike 2 Bike.
The land is free to bike on, but at one’s own risk. I rode with a group of roughly 12 people, and after making the trek, I would discourage anyone from going out there on their own.
Kudos to floor9.com for organizing this trip.

Only 8 miles of the 13 miles are accessible, as each end remains the private property of the turnpike commission. As you can see, the condition of the road is slowly deteriorating.

The entryway to the Rays Hill Tunnel. The doors to these tunnels were either soldered shut or missing.

The Sideling Hill Tunnel is roughly 1.3 miles long and the Rays Hill Tunnel is ~0.5 miles long. Bike headlights are a must, especially with Sideling Hill, as you can't see the end of the opposite side.

We took the steps up to the top of the tunnel where we got to see the huge exhaust fans. These fans were found above each end of the tunnel. Sideling Hill's fans were painted with graffiti. The entrance way (center) leads to an access tunnel above the road.

Above each tunnel runs an equally long access tunnel. Support beams run across the center, and a rusted track remains for what could possibly have been a rail car. This portion was a bit treacherous, as, despite what this long exposure displays, the hall was pitch black, and large holes in the floor remain where lights once hung.

Here's a shot from inside the tunnel, looking east. Some of the square "windows" showed the bedrock. From what I've been told, these tunnels originated as railway tunnels, and the current tunnel doesn't actually touch the underlying rock.

Here are the Rays Hill Tunnel exhaust fans.

These fans showed quite a bit of rust, but not much graffiti. This was probably due to the fact that the staircase had rusted and collapsed. Some of us crawled through the top and worked our way down.

At the end of the 8-mile stretch is PA Turnpike-owned private property. Apparently, a bridge spanned the gap to the east connection, but was torn down in 2005 since it was no longer structually sound.
Woodstock ’99
Found this in a dresser drawer at my parents’ house. I wonder how many folks still have one of these intact…

About Me
I'm a designer, developer, and teacher based in Harrisburg, Pa. I run Hauck Interactive, Inc.
Categories
Archives
- January 2012
- December 2011
- October 2011
- August 2011
- July 2011
- June 2011
- May 2011
- March 2011
- February 2011
- December 2010
- November 2010
- October 2010
- September 2010
- August 2010
- July 2010
- June 2010
- May 2010
- April 2010
- March 2010
- February 2010
- January 2010
- December 2009
- November 2009
- October 2009
- September 2009
- August 2009
- July 2009
- June 2009
- May 2009
- April 2009
- March 2009
- February 2009
- January 2009
- December 2008
- November 2008
- October 2008
- September 2008
- August 2008
- July 2008
- June 2008
- May 2008
- April 2008
- March 2008
- February 2008
- January 2008
- December 2007
- November 2007
- October 2007
- September 2007
- August 2007
- July 2007
- June 2007
- May 2007
- April 2007
- March 2007
- February 2007
- January 2007
- December 2006
- November 2006
- October 2006
- September 2006
- August 2006
- July 2006
- June 2006
- May 2006
- April 2006
- March 2006
- February 2006
- January 2006
- December 2005
- November 2005
- October 2005
- September 2005




