Your SlideShare is downloading. ×
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
High Performance JavaScript (CapitolJS 2011)
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

High Performance JavaScript (CapitolJS 2011)

14,370

Published on

Published in: Technology, Education
6 Comments
64 Likes
Statistics
Notes
  • Thank you, very useful.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • @HYCLEN No Spam!
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • 11-Bom dia amigos, Venham conhecer nossos Servidores de Mu Online
    O Jogando.net/mu Mu Online Season 6
    Fazemos sua Diversão com qualidade,há mais de 5 anos
    Servers ON 24 horas por dia.
    Vários Server esperando por você.Venha se divertir de verdade.
    Venham participar do 6° Megaultrasuperhiper Evento Castle Siege
    Sejam benvindos ao nosso Servidor.
    >>Kits DIAMOND,Com asas LVL 4,os kits mais fortes e raros do servidor ,venham conferir.
    >>Qual o seu Time ?? Tenha o seu Time do coração no Mu Online.Sets do seu Time preferido
    >>Confiram a nova versão do Shild mais usado no servidor o SHILD POWER v3.
    >> Lançamento do NOVO Kit Fusion V2,Kit hiper top e bonito.
    Você só encontra no http://www.jogando.net/mu/
    Facebook: http://www.facebook.com/profile.php?id=100002113910611
    Site http://www.jogando.net/mu/ HYCLEN Divulgadora Oficial !!!
    Boa Semana á todos !!!
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Yes, High Performance Javascript
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • ...Excellent....I got a $829.99 iPad2 for only $103.37 and my mom got a $1499.99 HDTV for only $251.92, they are both coming with USPS tomorrow. I would be an idiot to ever pay full retail prices at places like Walmart or Bestbuy. I sold a 37' HDTV to my boss for $600 that I only paid $78.24 for.I use.
    http://xub.me/Lb
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total Views
14,370
On Slideshare
0
From Embeds
0
Number of Embeds
8
Actions
Shares
0
Downloads
371
Comments
6
Likes
64
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide
  • Over the past couple of years, we've seen JavaScript development earn recognition as a true discipline. The idea that you should architect your code, use patterns and good programming practices has really elevated the role of the front end engineer. In my opinion, part of this elevation has been the adoption of what has traditionally been considered back end methodologies. We now focus on performance and algorithms, there's unit testing for JavaScript, and so much more. One of the areas that I've seen a much slower than adoption that I'd like is in the area of error handling.How many people have an error handling strategy for their backend? How many have dashboards that display problems with uptime and performance? How many have anything similar for the front end?Typically, the front end has been this black hole of information. You may get a few customer reports here and there, but you have no information about what's going on, how often it's occurring, or how many people have been affected.
  • So what have we talked about? Maintainable JavaScript is made up of four components.First is Code Conventions that describe the format of the code you’re writing.Second is Loose Coupling – keeping HTML, JavaScript, and CSS on separate layers and keeping application logic out of event handlers.Third is Programming Practices that ensure your code is readable and easily debugged.Fourth is creating a Build Process
  • Transcript

    • 1. High Performance JavaScript
      Nicholas C. Zakas| CapitolJS | September 18, 2011
    • 2. Who's this guy?
      Co-Creator csslint.net
      Contributor,
      Creator of YUI Test
      Ex-tech lead
      Yahoo!
      Author
      Lead Author
      Contributor
      Lead Author
    • 3. @slicknet
      (complaints: @voodootikigod)
    • 4. Does JavaScript performance matter?
    • 5.
    • 6.
    • 7.
    • 8. All browsers now haveoptimizing JavaScript engines
      Tracemonkey/
      JaegarMonkey
      (3.5+)
      V8
      (all)
      Nitro
      (4+)
      Chakra
      (9+)
      Karakan
      (10.5+)
    • 9. http://ie.microsoft.com/testdrive/benchmarks/sunspider/default.html
    • 10. Old computers ran slow applications
      Small amounts of CPU power and memory
    • 11. New computers are generally faster but
      slow applications still exist
      More CPU + more memory = less disciplined application development
    • 12.
    • 13. Oh yeah, one more thing
    • 14.
    • 15.
    • 16.
    • 17. It's still possible to write slow JavaScript
    • 18. JavaScript performancedirectlyaffects user experience
    • 19. The UI Threadaka Browser Event Loop
    • 20. The browser UI thread is responsible for
      both UI updates and JavaScript execution
      Only one can happen at a time
    • 21. Jobs for UI updates and JavaScript execution
      are added to a UI queue
      Each job must wait in line for its turn to execute
    • 22. <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>
    • 23. Before Click
      UI Thread
      time
      UI Queue
    • 24. When Clicked
      UI Thread
      time
      UI Queue
      UI Update
      onclick
      UI Update
    • 25. When Clicked
      UI Thread
      UI Update
      time
      UI Queue
      onclick
      Draw down state
      UI Update
    • 26. When Clicked
      UI Thread
      onclick
      UI Update
      time
      UI Queue
      UI Update
    • 27. When Clicked
      UI Thread
      onclick
      UI Update
      UI Update
      time
      UI Queue
      Draw up state
    • 28. No UI updates while JavaScript is executing
    • 29. 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>
    • 30. A UI update must use the latest info available
    • 31. Long-running JavaScript=Unresponsive UI
    • 32. Responsive UI
      UI Thread
      JavaScript
      UI Update
      UI Update
      time
    • 33. Unresponsive UI
      UI Thread
      JavaScript
      UI Update
      UI Update
      time
    • 34. The longer JavaScript runs,the worse the user experience
    • 35. The runaway script timer prevents JavaScript
      from running for too long
      Each browser imposes its own limit (except Opera)
    • 36. Internet Explorer
    • 37. Firefox
    • 38. Safari
    • 39. Chrome
    • 40.
    • 41. 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
    • 42. 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
    • 43. Translation:No single JavaScript job should execute for more than 100ms to ensure a responsive UI
    • 44. Recommendation:Limit JavaScript execution to no more than 50msmeasured on IE6 :)
    • 45. Loadtime TechniquesDon't let JavaScript interfere with page load performance
    • 46. During page load, JavaScript takes more time on the UI thread
    • 47. <!doctype html>
      <html>
      <head>
      <title>Example</title>
      </head>
      <body>
      <p>Hello world!</p>
      <script src="foo.js"></script>
      <p>See ya!</p>
      </body>
      </html>
    • 48. Result
      UI Thread
      JavaScript
      UI Update
      UI Update
      time
    • 49. Result
      UI Thread
      foo.js
      See ya!
      Hello world!
      time
    • 50. Result
      UI Thread
      Download
      See ya!
      Hello world!
      Parse
      Run
      time
      The UI thread needs to wait for the script to
      download, parse, and run before continuing
    • 51. Result
      UI Thread
      Download
      See ya!
      Hello world!
      Parse
      Run
      Variable
      Constant
      Download time takes the longest and is variable
    • 52. Translation:The page doesn't render while JavaScript is downloading, parsing, or executing during page load
    • 53. <!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>
    • 54. Result
      UI Thread
      JavaScript
      UI Update
      UI Update
      JavaScript
      JavaScript
      time
      The more scripts to download in between UI
      updates, the longer the page takes to render
    • 55. Technique #1: Load scripts dynamically
    • 56. Basic Technique
      var script = document.createElement("script"),
      body;
      script.type = "text/javascript";
      script.src = "foo.js";
      body.insertBefore(script, body.firstChild);
      Dynamically loaded scripts are non-blocking
    • 57. Downloads no longer block the UI thread
    • 58. <!doctype html>
      <html>
      <head>
      <title>Example</title>
      </head>
      <body>
      <p>Hello world!</p>
      <script src="foo.js"></script>
      <p>See ya!</p>
      </body>
      </html>
    • 59. Using HTML <script>
      UI Thread
      Download
      See ya!
      Hello world!
      Parse
      Run
      time
    • 60. <!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>
    • 61. Using Dynamic Scripts
      UI Thread
      See ya!
      Hello world!
      UI Update
      Run
      time
      Download
      Parse
      Only code execution happens on the UI thread,
      which means less blocking of UI updates
    • 62. 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);
      }
    • 63. Usage
      loadScript("foo.js", function(){
      alert("Loaded!");
      });
    • 64. Timing Note:Script execution begins immediately after download and parse – timing of execution is not guaranteed
    • 65. Technique #2: Defer scripts
    • 66. <!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>
    • 67. Support for <script defer>
      ?
      5.0
      3.5
      7.0
      4.0
    • 68. Deferred scripts begin to download immediately, but don't execute until all UI updates complete
    • 69. Using <script defer>
      UI Thread
      See ya!
      Hello world!
      More UI
      Run
      More UI
      time
      Download
      Parse
      Similar to dynamic script nodes, but with a
      guarantee that execution will happen last
    • 70. Timing Note:Although scripts always execute after UI updates complete, the order of multiple <script defer> scripts is not guaranteed across browsers
    • 71. Technique #3: Asynchronous scripts
    • 72. <!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>
    • 73. Support for <script async>
      ?
      5.0
      3.6
      7.0
      10
    • 74. Asynchronous scripts behave a lot like dynamic scripts
    • 75. Using <script async>
      UI Thread
      See ya!
      Hello world!
      UI Update
      Run
      time
      Download
      Parse
      Download begins immediately and execution is
      slotted in at first available spot
    • 76. Note:Order of execution is explicitly not preserved for asynchronous scripts
    • 77. Runtime TechniquesWays to ensure JavaScript doesn't run away
    • 78. function processArray(items, process, callback){
      for (var i=0,len=items.length; i < len; i++){
      process(items[i]);
      }
      callback();
      }
    • 79. Technique #1: Timers
    • 80. //create a new timer and delay by 500ms
      setTimeout(function(){
      //code to execute here
      }, 500);
      setTimeout() schedules a function to be added to the UI queue after a delay
    • 81. 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);
      }
    • 82. When Clicked
      UI Thread
      time
      UI Queue
      UI Update
      onclick
      UI Update
    • 83. When Clicked
      UI Thread
      UI Update
      time
      UI Queue
      onclick
      UI Update
    • 84. When Clicked
      UI Thread
      onclick
      UI Update
      time
      UI Queue
      UI Update
    • 85. When Clicked
      UI Thread
      UI Update
      UI Update
      onclick
      time
      UI Queue
    • 86. After 25ms
      UI Thread
      UI Update
      UI Update
      onclick
      time
      UI Queue
      JavaScript
    • 87. After 25ms
      UI Thread
      JavaScript
      UI Update
      UI Update
      onclick
      time
      UI Queue
    • 88. After Another 25ms
      UI Thread
      JavaScript
      UI Update
      UI Update
      onclick
      time
      UI Queue
      JavaScript
    • 89. After Another 25ms
      UI Thread
      JavaScript
      JavaScript
      UI Update
      UI Update
      onclick
      time
      UI Queue
    • 90. Technique #2: Script Yielding
      NEW!
    • 91.
    • 92. //delay a function until after UI updates are done
      setImmediate(function(){
      //code to execute here
      });
      setImmediate() adds code to the UI queue after pending UI updates are finished
    • 93. Support for Script Yielding
      ?
      ?
      ?
      ?
      10
      msSetIntermediate()
    • 94. functionyieldingProcessArray(items, process, callback){
      //create a clone of the originalvartodo = items.concat();
      setImmediate(function(){
      var start = +new Date();
      do {
      process(todo.shift());
      } while (todo.length > 0 &&
      (+new Date() - start < 50));
      if (todo.length > 0){
      setImmediate(arguments.callee);
      } else {
      callback(items);
      }
      });
      }
    • 95. When Clicked
      UI Thread
      time
      UI Queue
      UI Update
      onclick
      UI Update
    • 96. When Clicked
      UI Thread
      UI Update
      time
      UI Queue
      onclick
      UI Update
    • 97. When Clicked
      UI Thread
      onclick
      UI Update
      time
      UI Queue
      UI Update
    • 98. When Clicked
      UI Thread
      UI Update
      UI Update
      onclick
      time
      UI Queue
    • 99. After Last UI Update
      UI Thread
      UI Update
      UI Update
      onclick
      time
      UI Queue
      JavaScript
    • 100. After Last UI Update
      UI Thread
      JavaScript
      UI Update
      UI Update
      onclick
      time
      UI Queue
    • 101. No Other UI Updates
      UI Thread
      JavaScript
      UI Update
      UI Update
      onclick
      time
      UI Queue
      JavaScript
    • 102. No Other UI Updates
      UI Thread
      JavaScript
      JavaScript
      UI Update
      UI Update
      onclick
      time
      UI Queue
    • 103. Technique #3: Web Workers
    • 104.
    • 105. Support for Web Workers
      10.6
      4.0
      3.5
      4.0
      10
    • 106. Web Workers
      Asynchronous JavaScript execution
      Execution happens outside the UI thread
      Doesn’t block UI updates
      Data-Driven API
      Data is serialized going into and out of the worker
      No access to DOM or BOM
      Separate execution environment
    • 107. //in page
      var worker = new Worker("process.js");
      worker.onmessage = function(event){
      useData(event.data);
      };
      worker.postMessage(values);
      //in process.js
      self.onmessage = function(event){
      var items = event.data;
      for (var i=0,len=items.length; i < len; i++){
      process(items[i]);
      }
      self.postMessage(items);
      };
    • 108. When Clicked
      UI Thread
      time
      UI Queue
      UI Update
      onclick
      UI Update
    • 109. When Clicked
      UI Thread
      UI Update
      time
      UI Queue
      onclick
      UI Update
    • 110. When Clicked
      UI Thread
      onclick
      UI Update
      time
      UI Queue
      UI Update
    • 111. When Clicked
      UI Thread
      onclick
      UI Update
      time
      UI Queue
      Worker Thread
      UI Update
    • 112. When Clicked
      UI Thread
      UI Update
      UI Update
      onclick
      time
      UI Queue
      Worker Thread
      JavaScript
    • 113. Worker Thread Complete
      UI Thread
      UI Update
      UI Update
      onclick
      time
      UI Queue
      onmessage
    • 114. Worker Thread Complete
      UI Thread
      onmessage
      UI Update
      UI Update
      onclick
      time
      UI Queue
    • 115. Recap
    • 116. The browser UI thread is responsible for
      both UI updates and JavaScript execution
      Only one can happen at a time
    • 117. Responsive UI
      UI Thread
      JavaScript
      UI Update
      UI Update
      time
    • 118. Unresponsive UI
      UI Thread
      JavaScript
      UI Update
      UI Update
      time
    • 119. Avoid Slow Loading JavaScript
      Dynamically created scripts
      Deferred scripts
      Asynchronous scripts
    • 120. Avoid Slow JavaScript
      Don't allow JavaScript to execute for more than 50ms
      Break up long JavaScript processes using:
      Timers
      Script Yielding (future)
      Web Workers
    • 121.
    • 122.
    • 123. Etcetera
      My blog: www.nczonline.net
      Twitter: @slicknet
      These Slides: slideshare.net/nzakas
      Hire us: projects@stubbornella.org
    • 124. Questions?
    • 125. 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/

    ×