Creative Coding

A Cauldron of Canvas, SVG, webGL and webAudio

Owen Fernley

What is Creative Coding

To explore and experiment in order to better create something functional.

Discovering HTML5

Goal Oriented Learning

List of Tools

  • canvas
  • SVG (d3.js)
  • webGL (three.js)
  • webAudio

Common Thread

  • rendering / drawing / playing
  • resizing
  • touch and click
  • timers and animation

HTML5 Canvas

Manipulate Pixels

Photoshop of the Web

Create a Scene


							var canvas = null;
							var context = null;
							
							window.onload = function() {
								canvas = document.getElementById("canvas");
								context = canvas.getContext('2d');
							}
							
							window.onresize = resize;
						

Resize (simple)


							function resize() {
								canvas.width = window.innerWidth;
								canvas.height = window.innerHeight;
							}
						

Resize (retina)


							function resize() {
								var devicePixelRatio = window.devicePixelRatio || 1;
								var backingStoreRatio = canvas.webkitBackingStorePixelRatio || canvas.mozBackingStorePixelRatio || canvas.msBackingStorePixelRatio || canvas.oBackingStorePixelRatio || canvas.backingStorePixelRatio || 1;
								
								var ratio = devicePixelRatio / backingStoreRatio;
								var w = window.innerWidth;
								var h = window.innerHeight;
								
								canvas.width = w*ratio;
								canvas.height = h*ratio;
								canvas.style.width = w + 'px';
								canvas.style.height = h + 'px';
							}
						

Selections in Canvas


							circles.forEach(function(d) {
								var diff = {
									"x":(mouse.x - d.x),
									"y":(mouse.y - d.y)
								}
								var dist = Math.sqrt(diff.x*diff.x + diff.y*diff.y);
								if (dist < d.r) {
									d.clicked = true; // circle was clicked
									break;
								}
							});
						

setTimeout


							
							function animate() {
								draw(); // draw and move things
								
								setTimeout(animate, 1000/60); // 60fps
							}
							
							window.onload = function() {
								canvas = document.getElementById("canvas");
								context = canvas.getContext('2d');
								
								animate();
							}
						

requestAnimationFrame


							
							function animate() {
								requestAnimationFrame(animate); // ~60fps
								
								draw(); // draw and move things (afterward)
							}
							
							window.onload = function() {
								canvas = document.getElementById("canvas");
								context = canvas.getContext('2d');
								
								animate();
							}
						

Add Movement


							var shape = {"x":0,"y":0,"w":200,"h":200};
							var dim = {}; // set in resize
							
							function draw() {
								context.fillStyle = '#00aaff';  // light blue
								context.fillRect(shape.x, shape.y, shape.w, shape.h);
							}
							function animate() {
								requestAnimationFrame(animate); // ~60fps
								
								shape.x += 1; // movement
								shape.y += 1;
								
								draw(); // draw and move things (afterward)
							}
						

Refresh the Scene


							function resize() {
								dim.w = window.innerWidth;
								dim.h = window.innerHeight;
							}
							
							function draw() {
								context.fillStyle = '#fff'; // actually it's black
								context.fillRect(0,0,dim.w,dim.h); // refresh!
								
								context.fillStyle = '#00aaff';
								context.fillRect(shape.x, shape.y, shape.w, shape.h);
							}
						

globalAlpha!


							function draw() {
								context.globalAlpha = 0.1;
								context.fillStyle = '#fff';
								context.fillRect(0,0,dim.w,dim.h); // refresh (?)
								
								context.globalAlpha = 1;
								context.fillStyle = '#00aaff';
								context.fillRect(shape.x, shape.y, shape.w, shape.h);
							}
						

SVG

Scalable Vector Graphics

Manipulate Vectors

Illustrator of the Web

d3.js

Documentation

Examples

Create a d3 Rectangle


							var svg = d3.select("body").append("svg")
								.attr('class','main') // important for selecting later
								.attr("width", w)
								.attr("height", h);
							
							svg.append("rect")
								.attr("width", 20)
								.attr("height", 20)
								.style("fill", "#f00");
								
						

typical d3.js

( unpack this and win )

							svg.selectAll("rect")
								.data(d3.range(x * y))
								.enter().append("rect")
								.attr("transform", translate)
								.attr("width", z)
								.attr("height", z)
								.style("fill", function(d) { return d3.hsl(d % x / x * 360, 1, Math.floor(d / x) / y); })
								.on("mouseover", mouseover); // note: easy selections

							function translate(d) {
								return "translate(" + (d % x) * z + "," + Math.floor(d / x) * z + ")";
							}
							
						

Resize


							var dim = {};
							
							function resize() {
								dim.w = window.innerWidth;
								dim.h = window.innerHeight;
								
								d3.select('.main') 
									.attr("width", dim.w)
									.attr("height", dim.h); // no append
									
								update(); // redraw everything to new dimensions
							}
							window.onresize = resize;
						

d3-timer


							function animate() {
								// requestAnimationFrame(animate); not needed
								
								draw(); 
							}
							
							window.onload = function() {
								canvas = document.getElementById("canvas");
								context = canvas.getContext('2d');
								
								t1 = d3.timer(animate); // https://github.com/d3/d3-timer
							}
						

d3-zoom


							svg.append("rect")
								.attr("fill", "none")
								.attr("pointer-events", "all")
								.attr("width", width)
								.attr("height", height)
								.call(d3.zoom().on("zoom", zoom));
								
							function zoom() {
								g.attr("transform", d3.event.transform);
							}
						

d3-force


							simulation = d3.forceSimulation(nodes)
								.force("x", d3.forceX().strength(0.02))
								.force("y", d3.forceY().strength(0.02))
								.force("collide", d3.forceCollide().radius(
									function(d) { return d.r + 0.5; }).iterations(2).strength(1)
								)
								.alphaTarget(0.3); // how hard it tries
						
check out this crazy entropy example

Putting it all together

webGL

GPU accelerated 3D graphics

Documentation

https://threejs.org/docs/

pay attention to updates - r91 as of April 18, 2018

Examples

https://threejs.org/examples/ (official)

https://stemkoski.github.io/Three.js (older but awesome)

Creating a Scene

 
 

Creating a Scene


							var scene = new THREE.Scene();
							var camera = new THREE.PerspectiveCamera(
								75, 
								window.innerWidth/window.innerHeight,
								0.1, 
								1000 
							);
						

Initialize the Renderer


							window.onload = function() {
								var renderer = new THREE.WebGLRenderer();
								renderer.setPixelRatio( window.devicePixelRatio ); // retina displays
								renderer.setSize( window.innerWidth, window.innerHeight );
								document.body.appendChild( renderer.domElement );
							}
						

Make a Cube!


							function addcube() {
								var geometry = new THREE.BoxGeometry( 1, 1, 1 );
								var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
								var cube = new THREE.Mesh( geometry, material );
								scene.add( cube );
							}
						

Resize


						function resize() {
							camera.aspect = window.innerWidth / window.innerHeight;
							camera.updateProjectionMatrix();
							renderer.setSize( window.innerWidth, window.innerHeight );
						}
						window.onresize = resize;
						

Animation


						var animate = function () {
							requestAnimationFrame( animate );

							cube.rotation.x += 0.1;
							cube.rotation.y += 0.1;

							renderer.render(scene, camera);
						};
						

Controls

Orbit Controls


						
						function render() {
							renderer.render( scene, camera );
						}
						function animate() {
							requestAnimationFrame( animate );
							controls.update();
							render();
						}
						var controls = new THREE.OrbitControls( camera, renderer.domElement );
						

Lights


						var light1 = new THREE.PointLight( 0xffffff );
							light1.position.set( -700, 1000, 300 );
							light1.intensity = 0.5;
						var light2 = new THREE.PointLight( 0xffffff );
							light2.position.set( 700, 1000, -300 );
							light2.intensity = 0.8;
						var light3 = new THREE.AmbientLight( 0x222222 );
	
						scene.add( light1 );
						scene.add( light2 );
						scene.add( light3 );
						

Selections

Raycasting Example

						var raycaster = new THREE.Raycaster();
						
						raycaster.setFromCamera( mouse, camera );
						var intersects = raycaster.intersectObjects( scene.children );
						for ( var i = 0; i < intersects.length; i++ ) {
							intersects[ i ].object.material.color.set( 0xff0000 );
						}
						

Geometry in 3D

Vector3( )


							var unitx = new THREE.Vector3(1,0,0);
							var unity = new THREE.Vector3(0,0,-1);
							var unitz = new THREE.Vector3(0,1,0);
							
							unitz.crossVectors(unitx,unity); // finds the cross product
							unitz.applyMatrix4(matrix); // rotates the vector
						

Matrix4( )


						// rotate about x-axis by radians
						var matrix = new THREE.Matrix4().identity();
						matrix.makeRotationAxis(unitz,radians);
						group.applyMatrix(matrix);
						
						// handy helper
						group.lookAt(unitz);
						

Groups


						var cubeA = new THREE.Mesh( geometry, material );
						var cubeB = new THREE.Mesh( geometry, material );
						
						cubeA.position.set( 100, 100, 0 );
						cubeB.position.set( -100, -100, 0 );

						//These cubes can now be rotated / scaled etc as a group
						var group = new THREE.Group();
						group.add( cubeA );
						group.add( cubeB );
							
						scene.add( group );
						

Converters


							function UXVtoGL(comp) {
								var Vect = new THREE.Vector3(comp[0], comp[2], -comp[1]);
								return Vect;  // returns graphical
							}
							function GLtoUX(Vect) {
								var comp = [Vect.x,-Vect.z,Vect.y];
								return comp; // returns cartesian
							}
						

Converter Test


							
							// user to graphical
							UXVtoGL([1,2,3])
							--> p {x: 1, y: 3, z: -2}
							
							// graphical to user
							GLtoUX(new THREE.Vector3(1,3,-2))
							--> (3) [1, 2, 3]
							
							var xunit = UXVtoGL([1,0,0]); // readable
						

webAudio

Audio in Your Browser

Note: We are at the stage where nearly all browsers support MP3 — for more details visit this page on media format browser compatibility.


							
							// you can access the audio from the audio tag
							var audio = document.getElementById('audio_element');
							
							// or you can create an audio element in javascript
							var audio = new Audio('audio_file.mp3');
							
							// then to play or stop...
							audio.stop();
							audio.play();
							
							// note there is no pause
						

WebAudio Concept

Audio Context and Source


							var AuContext = new (window.AudioContext 
								|| window.webkitAudioContext)(); // define audio context
							// Webkit/blink browsers need prefix, Safari won't work without window.
							
							// get an audio source from the DOM 
							var source = audioCtx.createMediaElementSource(audio);
							
							// or from a stream
							var source = audioCtx.createMediaStreamSource(stream);
							
						

Connect a Gain Node


							var gainNode = AuContext.createGain();
							gainNode.gain.value = 1;
							
							source.connect(gainNode);
							gainNode.connect(AuContext.destination);
							
						

Connect an Analyser


							var analyser = audioCtx.createAnalyser();
							
							source.connect(gainNode);
							gainNode.connect(analyser);
							analyser.connect(AuContext.destination);
						

Max MSP / Jitter

(not webAudio but you get the idea)

webAudio: other resources

Szynalski Blog – Things I Learned the Hard Way

Tone.js - webAudio library

webMIDI - it exists!

Panners and Listeners


						var listener = AuContext.listener;
						AuContext.listener.setPosition(camPos.x,camPos.y,camPos.z);
						AuContext.listener.setOrientation(dirVect.x, dirVect.y, dirVect.z, upVect.x, upVect.y, upVect.z);
						
						var panner = AuContext.createPanner();
						panner.distanceModel = "linear";
						panner.setPosition(0,200,0);
						
						// connecting
						source.connect(panner);
						panner.connect(gainNode);
						gainNode.connect(analyser);
						analyser.connect(AuContext.destination);
						

three.js - attach a listener to a camera


						// create an AudioListener and add it to the camera
						var listener = new THREE.AudioListener();
						camera.add( listener );
						
						

Thank You!

owenfernley.com