high performance javascript 2011

155
High Performance JavaScript Nicholas C. Zakas | @slicknet Nicholas C. Zakas | @slicknet

Upload: nicholas-zakas

Post on 17-May-2015

10.639 views

Category:

Technology


0 download

DESCRIPTION

For much of its existence, JavaScript has been slow. No one complained until developers created complex web applications with thousands of lines of JavaScript code. Although newer JavaScript engines have improved the situation, there’s still a lot to understand about what makes JavaScript slow and what you can do to speed up your code.

TRANSCRIPT

Page 1: High Performance JavaScript 2011

High Performance JavaScriptNicholas C. Zakas | @slicknetNicholas C. Zakas | @slicknet

Page 2: High Performance JavaScript 2011

Who's this guy?

Co-Creator csslint.net

Contributor,Creator of YUI Test

Author Lead Author Contributor Lead Author

Page 3: High Performance JavaScript 2011

@slicknet

Page 4: High Performance JavaScript 2011

Does JavaScript performance matter?

Page 5: High Performance JavaScript 2011
Page 6: High Performance JavaScript 2011
Page 7: High Performance JavaScript 2011
Page 8: High Performance JavaScript 2011

All browsers now haveoptimizing JavaScript engines

Tracemonkey/JaegarMonkey

(3.5+)

V8(all)

Nitro(4+)

Chakra (9+)

Karakan(10.5+)

Page 9: High Performance JavaScript 2011

http://ie.microsoft.com/testdrive/benchmarks/sunspider/default.html

Page 10: High Performance JavaScript 2011

Old computers ran slow applications Small amounts of CPU power and memory

Page 11: High Performance JavaScript 2011

New computers are generally faster butslow applications still exist

More CPU + more memory = less disciplined application development

Page 12: High Performance JavaScript 2011
Page 13: High Performance JavaScript 2011
Page 14: High Performance JavaScript 2011

Oh yeah, one more thing

Page 15: High Performance JavaScript 2011
Page 16: High Performance JavaScript 2011
Page 17: High Performance JavaScript 2011

http://jeftek.com/1942/motorola-xoom-sunspider-results/

Page 18: High Performance JavaScript 2011
Page 19: High Performance JavaScript 2011

It's still possible to write slow JavaScript

Page 20: High Performance JavaScript 2011

JavaScript performancedirectly

affects user experience

Page 21: High Performance JavaScript 2011

The UI ThreadThe brains of the operation

Page 22: High Performance JavaScript 2011

The browser UI thread is responsible forboth UI updates and JavaScript execution

Only one can happen at a time

Page 23: High Performance JavaScript 2011

Jobs for UI updates and JavaScript executionare added to a UI queue

Each job must wait in line for its turn to execute

Page 24: High Performance JavaScript 2011

<button id="btn" style="font-size: 30px; padding: 0.5em 1em">Click Me</button>

<script type="text/javascript">window.onload = function(){ document.getElementById("btn").onclick = function(){ //do something };};</script>

Page 25: High Performance JavaScript 2011

Before Click

UI Thread

UI Queuetimetime

Page 26: High Performance JavaScript 2011

When Clicked

UI Thread

UI Queuetimetime

onclickonclick

UI UpdateUI Update

UI UpdateUI Update

Page 27: High Performance JavaScript 2011

When Clicked

UI Thread

UI Queuetimetime

onclickonclick

UI UpdateUI Update

UI UpdateUI Update

Draw down stateDraw down state

Page 28: High Performance JavaScript 2011

When Clicked

UI Thread

UI Queuetimetime

onclickonclick

UI UpdateUI Update

UI UpdateUI Update

Page 29: High Performance JavaScript 2011

When Clicked

UI Thread

UI Queuetimetime

onclickonclick UI UpdateUI UpdateUI UpdateUI Update

Draw up stateDraw up state

Page 30: High Performance JavaScript 2011

No UI updates while JavaScript is executing

Page 31: High Performance JavaScript 2011

JavaScript May Cause UI Update

<button id="btn" style="font-size: 30px; padding: 0.5em 1em">Click Me</button>

<script type="text/javascript">window.onload = function(){ document.getElementById("btn").onclick = function(){ var div = document.createElement(“div”); div.className = “tip”; div.innerHTML = “You clicked me!”; document.body.appendChild(div); };};</script>

Page 32: High Performance JavaScript 2011

A UI update must use the latest info available

Page 33: High Performance JavaScript 2011

Long-running JavaScript=

Unresponsive UI

Page 34: High Performance JavaScript 2011

Responsive UI

UI Thread

timetime

JavaScriptJavaScript UI UpdateUI UpdateUI UpdateUI Update

Page 35: High Performance JavaScript 2011

Unresponsive UI

UI Thread

timetime

JavaScriptJavaScript UI UpdateUI UpdateUI UpdateUI Update

Page 36: High Performance JavaScript 2011

The longer JavaScript runs,the worse the user experience

Page 37: High Performance JavaScript 2011

The runaway script timer prevents JavaScriptfrom running for too long

Each browser imposes its own limit (except Opera)

Page 38: High Performance JavaScript 2011

Internet Explorer

Page 39: High Performance JavaScript 2011

Firefox

Page 40: High Performance JavaScript 2011

Safari

Page 41: High Performance JavaScript 2011

Chrome

Page 42: High Performance JavaScript 2011
Page 43: High Performance JavaScript 2011

Runaway Script Timer Limits• Internet Explorer: 5 million statements• Firefox: 10 seconds• Safari: 5 seconds• Chrome: Unknown, hooks into normal crash

control mechanism• Opera: none

Page 44: High Performance JavaScript 2011

Does JIT compiling help?

Page 45: High Performance JavaScript 2011

Interpreted JavaScript

UI Thread

timetime

InterpretInterpret

Page 46: High Performance JavaScript 2011

JITed JavaScript (1st Run)

UI Thread

timetime

CompileCompile ExecuteExecute

Page 47: High Performance JavaScript 2011

JITed JavaScript (After 1st Run)

UI Thread

timetime

ExecuteExecute

Page 48: High Performance JavaScript 2011

How Long Is Too Long?

“0.1 second [100ms] is about the limit for having the user feel that the system is reacting instantaneously, meaning that no special feedback is necessary except to display the result.”

- Jakob Nielsen

Page 49: High Performance JavaScript 2011

Translation:No single JavaScript job should execute for more than 100ms to

ensure a responsive UI

Page 50: High Performance JavaScript 2011

Recommendation:Limit JavaScript execution to no more

than 50ms

measured on IE6 :)

Page 51: High Performance JavaScript 2011

Loadtime TechniquesDon't let JavaScript interfere with page load performance

Page 52: High Performance JavaScript 2011

During page load, JavaScript takes more time on the UI thread

Page 53: High Performance JavaScript 2011

<!doctype html><html><head> <title>Example</title></head><body> <p>Hello world!</p> <script src="foo.js"></script> <p>See ya!</p></body></html>

Page 54: High Performance JavaScript 2011

Result

UI Thread

timetime

JavaScriptJavaScript UI UpdateUI UpdateUI UpdateUI Update

Page 55: High Performance JavaScript 2011

Result

UI Thread

timetime

foo.jsfoo.js See ya!See ya!Hello world!Hello world!

Page 56: High Performance JavaScript 2011

Result

UI Thread

timetime

DownloadDownload See ya!See ya!Hello world!Hello world! ParseParse RunRun

The UI thread needs to wait for the script todownload, parse, and run before continuing

Page 57: High Performance JavaScript 2011

Result

UI Thread

DownloadDownload See ya!See ya!Hello world!Hello world! ParseParse RunRun

Download time takes the longest and is variable

Variable Constant

Page 58: High Performance JavaScript 2011

Translation:The page doesn't render while

JavaScript is downloading, parsing, or executing during page load

Page 59: High Performance JavaScript 2011

<!doctype html><html><head> <title>Example</title></head><body> <script src="foo.js"></script> <p>Hello world!</p> <script src="bar.js"></script> <p>See ya!</p> <script src="baz.js"></script> <p>Uh oh!</p></body></html>

Page 60: High Performance JavaScript 2011

Result

UI Thread

timetime

JavaScriptJavaScript UI UpdateUI UpdateUI UpdateUI Update

The more scripts to download in between UIupdates, the longer the page takes to render

JavaScriptJavaScript JavaScriptJavaScript

Page 61: High Performance JavaScript 2011

Technique #1: Put scripts at the bottom

Page 62: High Performance JavaScript 2011
Page 63: High Performance JavaScript 2011

<!doctype html><html><head> <title>Example</title></head><body> <p>Hello world!</p> <p>See ya!</p> <script src="foo.js"></script></body></html>

Page 64: High Performance JavaScript 2011

Put Scripts at Bottom

UI Thread

timetime

JavaScriptJavaScriptUI UpdateUI UpdateUI UpdateUI Update

Even if there are multiple scripts, the pagerenders quickly

JavaScriptJavaScript JavaScriptJavaScript

Page 65: High Performance JavaScript 2011

Technique #2: Combine JavaScript files

Page 66: High Performance JavaScript 2011

<!doctype html><html><head> <title>Example</title></head><body> <p>Hello world!</p> <p>See ya!</p> <script src="foo.js"></script> <script src="bar.js"></script> <script src="baz.js"></script></body></html>

Page 67: High Performance JavaScript 2011

UI Thread

timetime

JavaScriptJavaScriptUI UpdateUI Update

Each script has overhead of downloading

JavaScriptJavaScript JavaScriptJavaScript

Page 68: High Performance JavaScript 2011

UI Thread

timetime

JavaScriptJavaScriptUI UpdateUI Update

Combining all of the files limits the networkoverhead and gets scripts onto the page faster

Page 69: High Performance JavaScript 2011

<!doctype html><html><head> <title>Example</title></head><body> <p>Hello world!</p> <p>See ya!</p> <script src="foo-and-bar-and-baz.js"></script></body></html>

Page 70: High Performance JavaScript 2011

Technique #3: Load scripts dynamically

Page 71: High Performance JavaScript 2011

var script = document.createElement("script"), body;script.type = "text/javascript";script.src = "foo.js";body.insertBefore(script, body.firstChild);

Basic Technique

Dynamically loaded scripts are non-blocking

Page 72: High Performance JavaScript 2011

Downloads no longer block the UI thread

Page 73: High Performance JavaScript 2011

<!doctype html><html><head> <title>Example</title></head><body> <p>Hello world!</p> <script src="foo.js"></script> <p>See ya!</p></body></html>

Page 74: High Performance JavaScript 2011

Using HTML <script>

UI Thread

timetime

DownloadDownload See ya!See ya!Hello world!Hello world! ParseParse RunRun

Page 75: High Performance JavaScript 2011

<!doctype html><html><head> <title>Example</title></head><body> <p>Hello world!</p> <script> var script = document.createElement("script"), body = document.body; script.type = "text/javascript"; script.src = "foo.js"; body.insertBefore(script, body.firstChild); </script> <p>See ya!</p><!-- more content --></body></html>

Page 76: High Performance JavaScript 2011

Using Dynamic Scripts

UI Thread

timetime

DownloadDownload

See ya!See ya!Hello world!Hello world!

ParseParse

RunRun

Only code execution happens on the UI thread,which means less blocking of UI updates

UI UpdateUI Update

Page 77: High Performance JavaScript 2011

function loadScript(url, callback){var script = document.createElement("script"), body = document.body;script.type = "text/javascript";

if (script.readyState){ //IE <= 8 script.onreadystatechange = function(){ if (script.readyState == "loaded" || script.readyState == "complete"){ script.onreadystatechange = null; callback(); } };} else { //Others script.onload = function(){ callback(); };}script.src = url;body.insertBefore(script, body.firstChild);

}

Page 78: High Performance JavaScript 2011

loadScript("foo.js", function(){ alert("Loaded!");});

Usage

Page 79: High Performance JavaScript 2011

Timing Note:Script execution begins immediately after download and parse – timing of

execution is not guaranteed

Page 80: High Performance JavaScript 2011

Using Dynamic Scripts

UI Thread

timetime

DownloadDownload

See ya!See ya!Hello world!Hello world!

ParseParse

RunRun

Depending on time to download and script size,execution may happen before next UI update

UI UpdateUI Update

Page 81: High Performance JavaScript 2011

Technique #4: Defer scripts

Page 82: High Performance JavaScript 2011

<!doctype html><html><head> <title>Example</title></head><body> <p>Hello world!</p> <script defer src="foo.js"></script> <p>See ya!</p> <!-- even more markup --></body></html>

Page 83: High Performance JavaScript 2011

7.07.03.53.5 5.05.0 ??4.04.0

Support for <script defer>

Page 84: High Performance JavaScript 2011

Deferred scripts begin to download immediately,

but don't execute until all UI updates complete

Page 85: High Performance JavaScript 2011

Using <script defer>

UI Thread

timetime

DownloadDownload

See ya!See ya!Hello world!Hello world!

ParseParse

RunRun

Similar to dynamic script nodes, but with aguarantee that execution will happen last

More UIMore UI More UIMore UI

Page 86: High Performance JavaScript 2011

Timing Note:Although scripts always execute after

UI updates complete, the order of multiple <script defer> scripts is not

guaranteed across browsers

Page 87: High Performance JavaScript 2011

Technique #5: Asynchronous scripts

Page 88: High Performance JavaScript 2011

<!doctype html><html><head> <title>Example</title></head><body> <p>Hello world!</p> <script async src="foo.js"></script> <p>See ya!</p> <!-- even more markup --></body></html>

Page 89: High Performance JavaScript 2011

7.07.03.63.6 5.05.0 ??1010

Support for <script async>

Page 90: High Performance JavaScript 2011

Asynchronous scripts behave a lot like dynamic scripts

Page 91: High Performance JavaScript 2011

Using <script async>

UI Thread

timetime

DownloadDownload

See ya!See ya!Hello world!Hello world!

ParseParse

RunRun

Download begins immediately and execution isslotted in at first available spot

UI UpdateUI Update

Page 92: High Performance JavaScript 2011

Note:Order of execution is explicitly not preserved for asynchronous scripts

Page 93: High Performance JavaScript 2011

Runtime TechniquesWays to ensure JavaScript doesn't run away

Page 94: High Performance JavaScript 2011

function processArray(items, process, callback){ for (var i=0,len=items.length; i < len; i++){ process(items[i]); } callback();}

Page 95: High Performance JavaScript 2011

Technique #1: Timers

Page 96: High Performance JavaScript 2011

//create a new timer and delay by 500mssetTimeout(function(){

//code to execute here

}, 500)

setTimeout() schedules a function to be added to the UI queue after a delay

Page 97: High Performance JavaScript 2011

function timedProcessArray(items, process, callback){ //create a clone of the original

var todo = items.concat(); setTimeout(function(){ var start = +new Date(); do { process(todo.shift()); } while (todo.length > 0 && (+new Date() - start < 50)); if (todo.length > 0){ setTimeout(arguments.callee, 25); } else { callback(items); } }, 25);}

Page 98: High Performance JavaScript 2011

When Clicked

UI Thread

UI Queuetimetime

onclickonclick

UI UpdateUI Update

UI UpdateUI Update

Page 99: High Performance JavaScript 2011

When Clicked

UI Thread

UI Queuetimetime

onclickonclick

UI UpdateUI Update

UI UpdateUI Update

Page 100: High Performance JavaScript 2011

When Clicked

UI Thread

UI Queuetimetime

onclickonclick

UI UpdateUI Update

UI UpdateUI Update

Page 101: High Performance JavaScript 2011

When Clicked

UI Thread

UI Queuetimetime

UI UpdateUI UpdateUI UpdateUI Update onclickonclick

Page 102: High Performance JavaScript 2011

After 25ms

UI Thread

UI Queuetimetime

UI UpdateUI UpdateUI UpdateUI Update onclickonclick

JavaScriptJavaScript

Page 103: High Performance JavaScript 2011

After 25ms

UI Thread

UI Queuetimetime

UI UpdateUI UpdateUI UpdateUI Update onclickonclick JavaScriptJavaScript

Page 104: High Performance JavaScript 2011

After Another 25ms

UI Thread

UI Queuetimetime

UI UpdateUI UpdateUI UpdateUI Update onclickonclick JavaScriptJavaScript

JavaScriptJavaScript

Page 105: High Performance JavaScript 2011

After Another 25ms

UI Thread

UI Queuetimetime

UI UpdateUI UpdateUI UpdateUI Update onclickonclick JavaScriptJavaScript JavaScriptJavaScript

Page 106: High Performance JavaScript 2011

Technique #2: Web Workers

Page 107: High Performance JavaScript 2011
Page 108: High Performance JavaScript 2011

Web Workers

● Asynchronous JavaScript execution● Execution happens outside of UI thread

● Not on the UI thread = no UI delays● Data-driven API

● Data is serialized when sending data into or out of Worker

● No access to DOM, BOM● Completely separate execution environment

Page 109: High Performance JavaScript 2011

//in pagevar worker = new Worker("process.js");worker.onmessage = function(event){ useData(event.data);};worker.postMessage(values);

//in process.jsself.onmessage = function(event){ var items = event.data; for (var i=0,len=items.length; i < len; i++){ process(items[i]); } self.postMessage(items);};

Page 110: High Performance JavaScript 2011

When Clicked

UI Thread

UI Queuetimetime

onclickonclick

UI UpdateUI Update

UI UpdateUI Update

Page 111: High Performance JavaScript 2011

When Clicked

UI Thread

UI Queuetimetime

onclickonclick

UI UpdateUI Update

UI UpdateUI Update

Page 112: High Performance JavaScript 2011

When Clicked

UI Thread

UI Queuetimetime

onclickonclick

UI UpdateUI Update

UI UpdateUI Update

Page 113: High Performance JavaScript 2011

When Clicked

UI Thread

UI Queuetimetime

onclickonclick

UI UpdateUI Update

UI UpdateUI Update

Worker Thread

Page 114: High Performance JavaScript 2011

When Clicked

UI Thread

UI Queuetimetime

UI UpdateUI UpdateUI UpdateUI Update onclickonclick

Worker Thread

JavaScriptJavaScript

Page 115: High Performance JavaScript 2011

Worker Thread Complete

UI Thread

UI Queuetimetime

UI UpdateUI UpdateUI UpdateUI Update onclickonclick

onmessageonmessage

Page 116: High Performance JavaScript 2011

Worker Thread Complete

UI Thread

UI Queuetimetime

UI UpdateUI UpdateUI UpdateUI Update onclickonclick onmessageonmessage

Page 117: High Performance JavaScript 2011

4.04.03.53.5 4.04.0 10.610.61010

Support for Web Workers

Page 118: High Performance JavaScript 2011

Repaint and ReflowHidden performance costs of common operations

Page 119: High Performance JavaScript 2011

Long UI updates=

Unresponsive UI

Page 120: High Performance JavaScript 2011

Unresponsive UI

UI Thread

timetime

JavaScriptJavaScript UI UpdateUI UpdateUI UpdateUI Update

Page 121: High Performance JavaScript 2011

JavaScript can cause long UI updates by triggering

repaint and reflow

Page 122: High Performance JavaScript 2011

A repaint occurs when a visual change doesn't require recalculation of layout

Changes to visibility, colors (text/background), background images, etc.

Page 123: High Performance JavaScript 2011

Repaint

<button id="btn" style="font-size: 30px; padding: 0.5em 1em">Click Me</button>

<script type="text/javascript">window.onload = function(){ document.getElementById("btn").onclick = function(){ this.style.color = "#ff0"; };};</script> Repaint!Repaint!

Page 124: High Performance JavaScript 2011

A reflow occurs when a visual change requires a change in layout

Initial page load ▪ browser resize ▪ DOM structure change ▪ layout style changelayout information retrieved

Page 125: High Performance JavaScript 2011

Reflow

<button id="btn" style="font-size: 30px; padding: 0.5em 1em">Click Me</button>

<script type="text/javascript">window.onload = function(){ document.getElementById("btn").onclick = function(){ var div = document.createElement(“div”); div.className = “tip”; div.innerHTML = “You clicked me!”; document.body.appendChild(div); };};</script> Reflow!Reflow!

Page 126: High Performance JavaScript 2011

Repaints and reflows are queued up as JavaScript executes

and then executed in order

Page 127: High Performance JavaScript 2011

Reflow

var list = document.getElementsByClassName("items")[0], i, item;

for (i=0; i < 10; i++){ item = document.createElement("li"); item.innerHTML = "Item #" + i; list.appendChild(item);}

Reflow x 10!Reflow x 10!

Page 128: High Performance JavaScript 2011

Limiting repaints/reflows improves overall performance

Page 129: High Performance JavaScript 2011

Technique #1Perform DOM manipulations

off-document

Page 130: High Performance JavaScript 2011

Off-Document Operations

• Fast because there's no repaint/reflow• Techniques:

– Remove element from the document, make changes, insert back into document

– Set element's display to “none”, make changes, set display back to default

– Build up DOM changes on a DocumentFragment then apply all at once

Page 131: High Performance JavaScript 2011

DocumentFragment• A document-like object• Not visually represented• Considered to be owned by the document from

which it was created• When passed to appendChild(), appends all

of its children rather than itself

Page 132: High Performance JavaScript 2011

DocumentFragmentvar list = document.getElementsByClassName("items")[0], fragment = document.createDocumentFragment(), i, item;

for (i=0; i < 10; i++){ item = document.createElement("li"); item.innerHTML = "Item #" + i; fragment.appendChild(item);}list.appendChild(fragment);

1 Reflow1 Reflow

Page 133: High Performance JavaScript 2011

Technique #2Group Style Changes

Page 134: High Performance JavaScript 2011

element.style.color = "red";element.style.height = "100px";element.style.fontSize = "25px";element.style.backgroundColor = "white";

Repaint!Repaint! Reflow!Reflow!

Reflow!Reflow!

Repaint!Repaint!

Page 135: High Performance JavaScript 2011

.active { color: red; height: 100px; fontSize: 25px; background-color: white;}

element.className = "active";

Reflow!Reflow!

Grouping Style Changes

Page 136: High Performance JavaScript 2011

var item = document.getElementById("myItem");item.style.cssText = "color:red;height:100px;" + "font-size:25px;background-color: white");

Reflow!Reflow!

Grouping Style Changes

Page 137: High Performance JavaScript 2011

Technique #3Avoid Accidental Reflow

Page 138: High Performance JavaScript 2011

element.width = "100px";

var width = element.offsetWidth;

Reflow!Reflow!

Accidental Reflow

Page 139: High Performance JavaScript 2011

What to do?• Minimize access to layout information

– offsetTop, offsetLeft, offsetWidth, offsetHeight– scrollTop, scrollLeft, scrollWidth, scrollHeight– clientTop, clientLeft, clientWidth, clientHeight– Most computed styles

• If a value is used more than once, store in local variable

Page 140: High Performance JavaScript 2011

Does hardware acceleration help?

Page 141: High Performance JavaScript 2011

Traditional Rendering

UI Thread

timetime

CompositingCompositing DrawingDrawing

Page 142: High Performance JavaScript 2011

timetime

Hardware Acceleration

UI Thread

PrepPrep

GPU

CompositingCompositing

Rendering info

WaitWait

DrawingDrawing

Signal complete

Page 143: High Performance JavaScript 2011

Recap

Page 144: High Performance JavaScript 2011

The browser UI thread is responsible forboth UI updates and JavaScript execution

Only one can happen at a time

Page 145: High Performance JavaScript 2011

Responsive UI

UI Thread

timetime

JavaScriptJavaScript UI UpdateUI UpdateUI UpdateUI Update

Page 146: High Performance JavaScript 2011

Unresponsive UI

UI Thread

timetime

JavaScriptJavaScript UI UpdateUI UpdateUI UpdateUI Update

Page 147: High Performance JavaScript 2011

Unresponsive UI

UI Thread

timetime

JavaScriptJavaScript UI UpdateUI UpdateUI UpdateUI Update

Page 148: High Performance JavaScript 2011

Avoid Slow Loading JavaScript• Put scripts at the bottom• Concatenate scripts into as few files as

possible• Choose the right way to load your scripts

– Dynamically created scripts– Deferred scripts– Asynchronous scripts

Page 149: High Performance JavaScript 2011

Avoid Slow JavaScript• Don't allow JavaScript to execute for more

than 50ms• Break up long JavaScript processes using:

– Timers– Web Workers

Page 150: High Performance JavaScript 2011

Avoid Long UI Updates• Be careful of repaint and reflow• Perform complex DOM operations off-

document– Remove elements and re-add them– Use DocumentFragment objects

• Group style changes together• Avoid accidental reflow

Page 151: High Performance JavaScript 2011

Don't be horrible

Page 152: High Performance JavaScript 2011

Do be awesome

Page 153: High Performance JavaScript 2011

Etcetera• My blog: www.nczonline.net• Twitter: @slicknet• These Slides: slideshare.net/nzakas• Hire us: [email protected]

Page 154: High Performance JavaScript 2011

Questions?

Page 155: High Performance JavaScript 2011

Creative Commons Images Used• http://www.flickr.com/photos/lrargerich/3115367361/

• http://www.flickr.com/photos/hippie/2406411610/

• http://www.flickr.com/photos/55733754@N00/3325000738/

• http://www.flickr.com/photos/eurleif/255241547/

• http://www.flickr.com/photos/off_the_wall/3444915939/

• http://www.flickr.com/photos/wwarby/3296379139/

• http://www.flickr.com/photos/derekgavey/4358797365/

• http://www.flickr.com/photos/mulad/286641998/

• http://www.flickr.com/photos/torley/2361164281/

• http://www.flickr.com/photos/ottoman42/455242/

• http://www.flickr.com/photos/goincase/3843348908/