diff --git a/package.json b/package.json
index 0420d71..a81ca6c 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,8 @@
"dependencies": {
"brace": "^0.5.1",
"classnames": "^2.1.2",
+ "glamor": "^3.0.0-2",
+ "glamorous": "^3.23.5",
"history": "^3.3.0",
"markdown": "^0.5.0",
"prop-types": "^15.5.10",
@@ -53,4 +55,4 @@
"webpack": "^2.6.1",
"webpack-dev-server": "^2.5.1"
}
-}
\ No newline at end of file
+}
diff --git a/presentations/js-performance/Abandonment.js b/presentations/js-performance/Abandonment.js
new file mode 100644
index 0000000..4df4b75
--- /dev/null
+++ b/presentations/js-performance/Abandonment.js
@@ -0,0 +1,13 @@
+import React from 'react';
+import Slide from './components/Slide';
+import Link from './components/Link';
+import Attribution from './components/Attribution';
+import Statement from './components/Statement';
+
+export default function () {
+ return (
+
+ 53% of mobile site visits are abandoned when pages take longer than 3 seconds to load
+ - Google Double Click
+ );
+}
diff --git a/presentations/js-performance/Animation.js b/presentations/js-performance/Animation.js
new file mode 100644
index 0000000..c3a4764
--- /dev/null
+++ b/presentations/js-performance/Animation.js
@@ -0,0 +1,14 @@
+import React from 'react';
+import css from 'glamor';
+import glamorous from 'glamorous';
+import Slide from './components/Slide';
+import Title from './components/Title';
+import eventLoopSrc from './images/event-loop.png';
+
+export default function () {
+ return (
+
+ Animation - 60fps
+
+ );
+}
diff --git a/presentations/js-performance/CostGuesstimate.js b/presentations/js-performance/CostGuesstimate.js
new file mode 100644
index 0000000..d980f6d
--- /dev/null
+++ b/presentations/js-performance/CostGuesstimate.js
@@ -0,0 +1,12 @@
+import React from 'react';
+import Slide from './components/Slide';
+import Title from './components/Title';
+import chartSrc from './images/perf-chart.png';
+
+export default function () {
+ return (
+
+ optimization cost guesstimate
+
+ );
+}
diff --git a/presentations/js-performance/DaveQuote.js b/presentations/js-performance/DaveQuote.js
new file mode 100644
index 0000000..62c5547
--- /dev/null
+++ b/presentations/js-performance/DaveQuote.js
@@ -0,0 +1,12 @@
+import React from 'react';
+import Slide from './components/Slide';
+import Attribution from './components/Attribution';
+import Title from './components/Title';
+
+export default function () {
+ return (
+
+ not measuring performance is the root of all evil
+ - Me
+ );
+}
diff --git a/presentations/js-performance/FrameworkSpecific.js b/presentations/js-performance/FrameworkSpecific.js
new file mode 100644
index 0000000..071ebc1
--- /dev/null
+++ b/presentations/js-performance/FrameworkSpecific.js
@@ -0,0 +1,11 @@
+import React from 'react';
+import Slide from './components/Slide';
+import Statement from './components/Statement';
+
+export default function () {
+ return (
+
+ react-addons-perf
+ ng-stats
+ );
+}
\ No newline at end of file
diff --git a/presentations/js-performance/Idle.js b/presentations/js-performance/Idle.js
new file mode 100644
index 0000000..a6c9a81
--- /dev/null
+++ b/presentations/js-performance/Idle.js
@@ -0,0 +1,15 @@
+import React from 'react';
+import css from 'glamor';
+import glamorous from 'glamorous';
+import Slide from './components/Slide';
+import Title from './components/Title';
+import Statement from './components/Statement';
+
+export default function () {
+ return (
+
+ Idle - maximize idle time
+ perform background tasks
+ pre-load resources
+ );
+}
diff --git a/presentations/js-performance/Intro.js b/presentations/js-performance/Intro.js
new file mode 100644
index 0000000..fda12de
--- /dev/null
+++ b/presentations/js-performance/Intro.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import Slide from './components/Slide';
+import bunnySrc from './images/bunny.gif';
+
+export default function () {
+ return (
+
+
+ );
+}
diff --git a/presentations/js-performance/KnuthQuote.js b/presentations/js-performance/KnuthQuote.js
new file mode 100644
index 0000000..ad3771d
--- /dev/null
+++ b/presentations/js-performance/KnuthQuote.js
@@ -0,0 +1,12 @@
+import React from 'react';
+import Slide from './components/Slide';
+import Attribution from './components/Attribution';
+import Title from './components/Title';
+
+export default function () {
+ return (
+
+ premature optimization is the root of all evil
+ - Donald Knuth
+ );
+}
diff --git a/presentations/js-performance/KnuthQuoteRevised.js b/presentations/js-performance/KnuthQuoteRevised.js
new file mode 100644
index 0000000..16c1f75
--- /dev/null
+++ b/presentations/js-performance/KnuthQuoteRevised.js
@@ -0,0 +1,12 @@
+import React from 'react';
+import Slide from './components/Slide';
+import Attribution from './components/Attribution';
+import Title from './components/Title';
+
+export default function () {
+ return (
+
+ don't optimize before you measure
+ - What Donald Knuth really meant
+ );
+}
diff --git a/presentations/js-performance/LazyCode.js b/presentations/js-performance/LazyCode.js
new file mode 100644
index 0000000..e391148
--- /dev/null
+++ b/presentations/js-performance/LazyCode.js
@@ -0,0 +1,26 @@
+import React from 'react';
+import Slide from './components/Slide';
+import Title from './components/Title';
+import CodeBlock from '../../lib/components/CodeBlock';
+
+const code =
+`function init() {
+ const playAudio = () => {
+ fetchAudio().then(audio => audio.play());
+ };
+
+ fetchBitmap().then(
+ bitmap => renderApp(bitmap, playAudio));
+}
+
+function fetchBitmap() { ... }
+
+function fetchAudio() { ... }`;
+
+export default function() {
+ return (
+
+ lazy code
+ { code }
+ );
+}
diff --git a/presentations/js-performance/Load.js b/presentations/js-performance/Load.js
new file mode 100644
index 0000000..059e485
--- /dev/null
+++ b/presentations/js-performance/Load.js
@@ -0,0 +1,36 @@
+import React from 'react';
+import css from 'glamor';
+import glamorous from 'glamorous';
+import Slide from './components/Slide';
+import Title from './components/Title';
+import Statement from './components/Statement';
+import unicornSrc from './components/SparkleApp/images/unicorn.png';
+
+const fadeIn = css.keyframes({
+ '0%': {opacity: 0},
+ '100%': {opacity: 0.5}
+});
+
+const Background = glamorous.div({
+ position: 'absolute',
+ backgroundImage: `url(${unicornSrc})`,
+ backgroundRepeat: 'space',
+ backgroundPosition: 'center',
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ opacity: 0.5,
+ animation: `${fadeIn} 5s ease-in`
+});
+
+export default function () {
+ return (
+
+ Load - 1000 ms
+ initial site load
+ data and content loading
+ page and view change
+
+ );
+}
diff --git a/presentations/js-performance/Measurement.js b/presentations/js-performance/Measurement.js
new file mode 100644
index 0000000..36af345
--- /dev/null
+++ b/presentations/js-performance/Measurement.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import Slide from './components/Slide';
+import Title from './components/Title';
+
+export default function () {
+ return (
+
+ develop a measurement plan
+ );
+}
diff --git a/presentations/js-performance/OptimizeFramerate.js b/presentations/js-performance/OptimizeFramerate.js
new file mode 100644
index 0000000..435d6d9
--- /dev/null
+++ b/presentations/js-performance/OptimizeFramerate.js
@@ -0,0 +1,13 @@
+import React from 'react';
+import Slide from './components/Slide';
+import Title from './components/Title';
+import SparkleApp from './components/SparkleApp';
+import { initWithLongFrames } from './components/SparkleApp/initialize';
+
+export default function() {
+ return (
+
+ optimize framerate (animation)
+
+ );
+}
diff --git a/presentations/js-performance/OptimizeIdle.js b/presentations/js-performance/OptimizeIdle.js
new file mode 100644
index 0000000..1e0da89
--- /dev/null
+++ b/presentations/js-performance/OptimizeIdle.js
@@ -0,0 +1,33 @@
+import React from 'react';
+import Slide from './components/Slide';
+import Title from './components/Title';
+import CodeBlock from '../../lib/components/CodeBlock';
+
+const code =
+`function init() {
+ fetchBitmap().then(
+ bitmap => {
+ const app = renderApp(bitmap);
+
+ const idleCallback = requestIdleCallback ||
+ requestAnimationFrame;
+
+ idleCallback(() => {
+ fetchAudio().then(
+ audio => app.setAudio(audio));
+ });
+ }
+ );
+}
+
+function fetchBitmap() { ... }
+
+function fetchAudio() { ... }`;
+
+export default function() {
+ return (
+
+ use idle time to pre-fetch
+ { code }
+ );
+}
diff --git a/presentations/js-performance/OptimizeLoad.js b/presentations/js-performance/OptimizeLoad.js
new file mode 100644
index 0000000..b15af33
--- /dev/null
+++ b/presentations/js-performance/OptimizeLoad.js
@@ -0,0 +1,13 @@
+import React from 'react';
+import Slide from './components/Slide';
+import Title from './components/Title';
+import SparkleApp from './components/SparkleApp';
+import { initWithLargeImage } from './components/SparkleApp/initialize';
+
+export default function() {
+ return (
+
+ optimize load time
+
+ );
+}
diff --git a/presentations/js-performance/OptimizeNetwork.js b/presentations/js-performance/OptimizeNetwork.js
new file mode 100644
index 0000000..8d519f3
--- /dev/null
+++ b/presentations/js-performance/OptimizeNetwork.js
@@ -0,0 +1,14 @@
+import React from 'react';
+import Slide from './components/Slide';
+import Statement from './components/Statement';
+
+export default function () {
+ return (
+
+ minimize file count and size
+ configure cache headers
+ configure compression
+ use http2
+ use a CDN
+ );
+}
diff --git a/presentations/js-performance/OptimizeResponse.js b/presentations/js-performance/OptimizeResponse.js
new file mode 100644
index 0000000..116d827
--- /dev/null
+++ b/presentations/js-performance/OptimizeResponse.js
@@ -0,0 +1,13 @@
+import React from 'react';
+import Slide from './components/Slide';
+import Title from './components/Title';
+import SparkleApp from './components/SparkleApp';
+import { initWithLazyAudio } from './components/SparkleApp/initialize';
+
+export default function() {
+ return (
+
+ optimize response time
+
+ );
+}
diff --git a/presentations/js-performance/OptimizedApp.js b/presentations/js-performance/OptimizedApp.js
new file mode 100644
index 0000000..ca5963a
--- /dev/null
+++ b/presentations/js-performance/OptimizedApp.js
@@ -0,0 +1,12 @@
+import React from 'react';
+import Slide from './components/Slide';
+import Title from './components/Title';
+import SparkleApp from './components/SparkleApp';
+
+export default function () {
+ return (
+
+ RAIL optimized
+
+ );
+}
diff --git a/presentations/js-performance/PageSpeedTools.js b/presentations/js-performance/PageSpeedTools.js
new file mode 100644
index 0000000..65f607d
--- /dev/null
+++ b/presentations/js-performance/PageSpeedTools.js
@@ -0,0 +1,12 @@
+import React from 'react';
+import Slide from './components/Slide';
+import Title from './components/Title';
+import pstSrc from './images/page-speed-tools.png';
+
+export default function () {
+ return (
+
+ PageSpeed Tools (developers.google.com/speed)
+
+ );
+}
\ No newline at end of file
diff --git a/presentations/js-performance/ParallelCode.js b/presentations/js-performance/ParallelCode.js
new file mode 100644
index 0000000..7bb151c
--- /dev/null
+++ b/presentations/js-performance/ParallelCode.js
@@ -0,0 +1,22 @@
+import React from 'react';
+import Slide from './components/Slide';
+import Title from './components/Title';
+import CodeBlock from '../../lib/components/CodeBlock';
+
+const code =
+`function init() {
+ Promise.all([fetchBitmap(), fetchAudio()])
+ .then([bitmap, audio] => renderApp(bitmap, audio));
+}
+
+function fetchBitmap() { ... }
+
+function fetchAudio() { ... }`;
+
+export default function() {
+ return (
+
+ parallel code
+ { code }
+ );
+}
diff --git a/presentations/js-performance/Plan.js b/presentations/js-performance/Plan.js
new file mode 100644
index 0000000..1dfaa3f
--- /dev/null
+++ b/presentations/js-performance/Plan.js
@@ -0,0 +1,12 @@
+import React from 'react';
+import Slide from './components/Slide';
+import Statement from './components/Statement';
+
+export default function () {
+ return (
+
+ Load and render app in < 1 second
+ Respond to Sparkle! in < 100ms
+ Animate sparkles at 60fps
+ );
+}
diff --git a/presentations/js-performance/Rail.js b/presentations/js-performance/Rail.js
new file mode 100644
index 0000000..af3a177
--- /dev/null
+++ b/presentations/js-performance/Rail.js
@@ -0,0 +1,30 @@
+import React from 'react';
+import glamorous from 'glamorous';
+import Slide from './components/Slide';
+import Title from './components/Title';
+
+const Measurement = glamorous.div({
+ textAlign: 'left',
+ width: '800px'
+});
+const Label = glamorous.div({
+ fontSize: '32px'
+});
+const Emphasis = glamorous.span({
+ fontSize: '96px',
+ fontWeight: 'bold',
+ marginRight: '8px'
+});
+
+export default function () {
+ return (
+
+ RAIL model
+
+
+
+
+
+
+ );
+}
diff --git a/presentations/js-performance/RemoveSequential.js b/presentations/js-performance/RemoveSequential.js
new file mode 100644
index 0000000..0864cac
--- /dev/null
+++ b/presentations/js-performance/RemoveSequential.js
@@ -0,0 +1,13 @@
+import React from 'react';
+import Slide from './components/Slide';
+import Title from './components/Title';
+import SparkleApp from './components/SparkleApp';
+import { initWithSequentialAudio } from './components/SparkleApp/initialize';
+
+export default function() {
+ return (
+
+ remove sequential blocking code
+
+ );
+}
diff --git a/presentations/js-performance/Response.js b/presentations/js-performance/Response.js
new file mode 100644
index 0000000..81d2594
--- /dev/null
+++ b/presentations/js-performance/Response.js
@@ -0,0 +1,15 @@
+import React from 'react';
+import css from 'glamor';
+import glamorous from 'glamorous';
+import Slide from './components/Slide';
+import Title from './components/Title';
+import Statement from './components/Statement';
+
+export default function () {
+ return (
+
+ Response - 100ms
+ users perceive delays over 100ms
+ > 500ms should display indicator
+ );
+}
diff --git a/presentations/js-performance/SequentialCode.js b/presentations/js-performance/SequentialCode.js
new file mode 100644
index 0000000..ac99713
--- /dev/null
+++ b/presentations/js-performance/SequentialCode.js
@@ -0,0 +1,25 @@
+import React from 'react';
+import Slide from './components/Slide';
+import Title from './components/Title';
+import CodeBlock from '../../lib/components/CodeBlock';
+
+const code =
+`function init() {
+ fetchBitmap().then(bitmap => {
+ return fetchAudio().then(audio => {
+ renderApp(bitmap, audio);
+ });
+ });
+}
+
+function fetchBitmap() { ... }
+
+function fetchAudio() { ... }`;
+
+export default function() {
+ return (
+
+ sequential code
+ { code }
+ );
+}
diff --git a/presentations/js-performance/Sparkle.js b/presentations/js-performance/Sparkle.js
new file mode 100644
index 0000000..ef3db8c
--- /dev/null
+++ b/presentations/js-performance/Sparkle.js
@@ -0,0 +1,12 @@
+import React from 'react';
+import Slide from './components/Slide';
+import Title from './components/Title';
+import SparkleApp from './components/SparkleApp';
+
+export default function () {
+ return (
+
+ Sparkle! the app
+
+ );
+}
diff --git a/presentations/js-performance/UserExpectation.js b/presentations/js-performance/UserExpectation.js
new file mode 100644
index 0000000..7b9c451
--- /dev/null
+++ b/presentations/js-performance/UserExpectation.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import Slide from './components/Slide';
+import Title from './components/Title';
+
+export default function () {
+ return (
+
+ content vs responsiveness
+ );
+}
diff --git a/presentations/js-performance/WebPageTest.js b/presentations/js-performance/WebPageTest.js
new file mode 100644
index 0000000..8cc0c66
--- /dev/null
+++ b/presentations/js-performance/WebPageTest.js
@@ -0,0 +1,12 @@
+import React from 'react';
+import Slide from './components/Slide';
+import Title from './components/Title';
+import wptSrc from './images/web-page-test.png';
+
+export default function () {
+ return (
+
+ WebPageTest (www.webpagetest.org)
+
+ );
+}
\ No newline at end of file
diff --git a/presentations/js-performance/components/Attribution.js b/presentations/js-performance/components/Attribution.js
new file mode 100644
index 0000000..e991c6f
--- /dev/null
+++ b/presentations/js-performance/components/Attribution.js
@@ -0,0 +1,6 @@
+import glamorous from 'glamorous';
+
+export default glamorous.h3({
+ textAlign: 'right',
+ marginRight: '100px'
+});
\ No newline at end of file
diff --git a/presentations/js-performance/components/Link.js b/presentations/js-performance/components/Link.js
new file mode 100644
index 0000000..1c2c3b7
--- /dev/null
+++ b/presentations/js-performance/components/Link.js
@@ -0,0 +1,13 @@
+import glamorous from 'glamorous';
+
+const color = '#999';
+const activeColor = '#FF80E1';
+
+export default glamorous.a({
+ color,
+ ':hover': { color: activeColor },
+ ':visited': {
+ color,
+ ':hover': { color: activeColor }
+ }
+});
\ No newline at end of file
diff --git a/presentations/js-performance/components/Slide.js b/presentations/js-performance/components/Slide.js
new file mode 100644
index 0000000..b10b549
--- /dev/null
+++ b/presentations/js-performance/components/Slide.js
@@ -0,0 +1,3 @@
+import glamorous from 'glamorous';
+
+export default glamorous.div();
\ No newline at end of file
diff --git a/presentations/js-performance/components/SparkleApp/ParticleCloud.js b/presentations/js-performance/components/SparkleApp/ParticleCloud.js
new file mode 100644
index 0000000..2a3a168
--- /dev/null
+++ b/presentations/js-performance/components/SparkleApp/ParticleCloud.js
@@ -0,0 +1,107 @@
+const colorPalette = [
+ '#029DAF',
+ '#E5D599',
+ '#FFC219',
+ '#FF80E1'
+];
+
+function isInBounds(config, particle) {
+ const { canvas } = config;
+
+ if (config.longFrame) {
+ const calculateBounds = (delay) => {
+ const start = Date.now();
+ while (Date.now() - start < delay) {}
+ }
+ calculateBounds(3);
+ }
+
+ return particle.x - particle.size < canvas.width &&
+ particle.x + particle.size > 0 &&
+ particle.y - particle.size < canvas.height &&
+ particle.y + particle.size > 0;
+}
+
+function drawParticle(config, particle) {
+ const { canvas, ctx, gravity, velocity } = config;
+
+ particle.vy += 0.04 * velocity;
+ particle.x += particle.vx * velocity;
+ particle.y += particle.vy * velocity;
+ particle.opacity -= 0.005;
+
+ ctx.globalAlpha = particle.opacity;
+ ctx.fillStyle = particle.color;
+ ctx.fillRect(particle.x, particle.y, particle.size, particle.size);
+
+ return isInBounds(config, particle);
+}
+
+function initParticles(config) {
+ const particles = [];
+ for (let i = 0; i < config.particleCount; i++) {
+ const colorIdx = i % colorPalette.length;
+ const particle = {
+ color: colorPalette[colorIdx],
+ opacity: 0.5 + Math.random() * 0.5,
+ size: 6 + Math.random() * 6,
+ x: config.startX,
+ y: config.startY,
+ vx: (Math.random() * 3) + 1,
+ vy: (Math.random() * -2) - 1
+ };
+
+ particles.push(particle);
+ }
+
+ return particles;
+}
+
+function drawBackground(config) {
+ const { canvas, ctx, backgroundBitmap } = config;
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ ctx.globalAlpha = 1;
+ ctx.drawImage(backgroundBitmap, 50, 0, 386, 500);
+}
+
+export default class ParticleCloud {
+ constructor(canvas, backgroundBitmap, longFrame = false) {
+ this.config = {
+ canvas,
+ ctx: canvas.getContext('2d'),
+ longFrame,
+ particleCount: 50,
+ backgroundBitmap,
+ startX: 280,
+ startY: 320,
+ velocity: 1
+ };
+
+ drawBackground(this.config);
+ }
+
+ animate() {
+ this.activeParticles = (this.activeParticles || []).concat(initParticles(this.config));
+
+ if (!this._animate) {
+ this._animate = () => {
+ if (this._animate) {
+ const { ctx } = this.config;
+
+ drawBackground(this.config);
+ this.activeParticles = this.activeParticles.filter((particle) => {
+ return drawParticle(this.config, particle);
+ });
+
+ window.requestAnimationFrame(this._animate);
+ }
+ };
+ this._animate();
+ }
+ }
+
+ end() {
+ this._animate = null;
+ }
+}
diff --git a/presentations/js-performance/components/SparkleApp/audio/taste-the-rainbow.m4a b/presentations/js-performance/components/SparkleApp/audio/taste-the-rainbow.m4a
new file mode 100644
index 0000000..6ec7821
Binary files /dev/null and b/presentations/js-performance/components/SparkleApp/audio/taste-the-rainbow.m4a differ
diff --git a/presentations/js-performance/components/SparkleApp/images/unicorn-large.png b/presentations/js-performance/components/SparkleApp/images/unicorn-large.png
new file mode 100644
index 0000000..1252e88
Binary files /dev/null and b/presentations/js-performance/components/SparkleApp/images/unicorn-large.png differ
diff --git a/presentations/js-performance/components/SparkleApp/images/unicorn.png b/presentations/js-performance/components/SparkleApp/images/unicorn.png
new file mode 100644
index 0000000..adb7824
Binary files /dev/null and b/presentations/js-performance/components/SparkleApp/images/unicorn.png differ
diff --git a/presentations/js-performance/components/SparkleApp/index.js b/presentations/js-performance/components/SparkleApp/index.js
new file mode 100644
index 0000000..6d94b7a
--- /dev/null
+++ b/presentations/js-performance/components/SparkleApp/index.js
@@ -0,0 +1,100 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import glamorous from 'glamorous';
+import ParticleCloud from './ParticleCloud';
+import { initOptimized } from './initialize';
+
+const SparkleButton = glamorous.button({
+ fontSize: '32px',
+ backgroundColor: '#FF80E1',
+ borderRadius: '4px',
+ width: '100%',
+ zIndex: 100
+});
+
+function sparkle(audio, particleCloud) {
+ if (audio) {
+ audio.currentTime = 0;
+ audio.play();
+ }
+
+ if (particleCloud) {
+ particleCloud.animate();
+ }
+}
+
+export default class SparkleApp extends React.Component {
+ static defaultProps = {
+ initResources: initOptimized
+ };
+
+ constructor(props) {
+ super(props);
+
+ this.canvasRef = this.canvasRef.bind(this);
+ this.onSparkleClick = this.onSparkleClick.bind(this);
+ }
+
+ componentDidMount() {
+ this.props.initResources().then(resources => {
+ // support different methods of audio initialization for demo purposes.
+ if (resources.idleAudio) {
+ const idleCallback = requestIdleCallback || requestAnimationFrame;
+ idleCallback(() => {
+ resources.idleAudio().then(audio => {
+ this.audio = audio;
+ });
+ });
+ } else if (resources.lazyAudio) {
+ this.lazyAudio = resources.lazyAudio;
+ } else if (resources.audio) {
+ this.audio = resources.audio;
+ }
+
+ this.particleCloud = new ParticleCloud(
+ this.canvas,
+ resources.bitmap,
+ resources.longFrame || false);
+ });
+ }
+
+ componentWillUnmount() {
+ if (this.particleCloud) {
+ this.particleCloud.end();
+ this.particleCloud = null;
+ }
+
+ if (this.audio) {
+ this.audio = null;
+ }
+ }
+
+ render() {
+ return (
+
+
+
+ Sparkle!
+
+
+ );
+ }
+
+ canvasRef(canvas) {
+ this.canvas = canvas;
+ }
+
+ onSparkleClick(e) {
+ e.stopPropagation();
+ e.preventDefault();
+
+ if (this.lazyAudio) {
+ this.lazyAudio().then(audio => {
+ sparkle(audio, this.particleCloud);
+ });
+ } else if (this.audio) {
+ sparkle(this.audio, this.particleCloud);
+ }
+ }
+}
\ No newline at end of file
diff --git a/presentations/js-performance/components/SparkleApp/initialize.js b/presentations/js-performance/components/SparkleApp/initialize.js
new file mode 100644
index 0000000..a1d41fc
--- /dev/null
+++ b/presentations/js-performance/components/SparkleApp/initialize.js
@@ -0,0 +1,74 @@
+import largeUnicornSrc from './images/unicorn-large.png';
+import unicornSrc from './images/unicorn.png';
+import rainbowSrc from './audio/taste-the-rainbow.m4a';
+
+const slowAudioDelay = 900;
+
+function loadBitmap(imgSrc) {
+ return fetch(imgSrc).then(response => {
+ return response.blob().then(window.createImageBitmap);
+ });
+}
+
+function createAudio(blob, delay) {
+ const bufferAudio = (delay) => {
+ const start = Date.now();
+ while (Date.now() - start < delay) {}
+ }
+ bufferAudio(delay);
+
+ return new Audio(URL.createObjectURL(blob));
+}
+
+function loadAudio(delay = 0) {
+ return fetch(rainbowSrc).then(response => {
+ return response.blob().then(blob => createAudio(blob, delay));
+ });
+}
+
+export function initWithLargeImage() {
+ return loadBitmap(largeUnicornSrc).then(bitmap => {
+ return loadAudio(slowAudioDelay).then(audio => {
+ return {
+ audio,
+ bitmap
+ };
+ });
+ });
+}
+
+export function initWithSequentialAudio() {
+ return loadBitmap(unicornSrc).then(bitmap => {
+ return loadAudio(slowAudioDelay).then(audio => {
+ return {
+ audio,
+ bitmap
+ };
+ });
+ });
+}
+
+export function initWithLazyAudio() {
+ return loadBitmap(unicornSrc).then(bitmap => {
+ return {
+ lazyAudio: () => loadAudio(slowAudioDelay),
+ bitmap
+ };
+ });
+}
+
+export function initWithLongFrames() {
+ return initOptimized().then(resources => {
+ resources.longFrame = true;
+ return resources;
+ });
+}
+
+export function initOptimized() {
+ return loadBitmap(unicornSrc).then(bitmap => {
+ return {
+ idleAudio: loadAudio,
+ bitmap
+ };
+ });
+}
\ No newline at end of file
diff --git a/presentations/js-performance/components/Statement.js b/presentations/js-performance/components/Statement.js
new file mode 100644
index 0000000..83acc12
--- /dev/null
+++ b/presentations/js-performance/components/Statement.js
@@ -0,0 +1,3 @@
+import glamorous from 'glamorous';
+
+export default glamorous.h2();
\ No newline at end of file
diff --git a/presentations/js-performance/components/Title.js b/presentations/js-performance/components/Title.js
new file mode 100644
index 0000000..3972839
--- /dev/null
+++ b/presentations/js-performance/components/Title.js
@@ -0,0 +1,3 @@
+import glamorous from 'glamorous';
+
+export default glamorous.h1();
\ No newline at end of file
diff --git a/presentations/js-performance/images/bunny.gif b/presentations/js-performance/images/bunny.gif
new file mode 100644
index 0000000..bb15327
Binary files /dev/null and b/presentations/js-performance/images/bunny.gif differ
diff --git a/presentations/js-performance/images/event-loop.png b/presentations/js-performance/images/event-loop.png
new file mode 100644
index 0000000..1ee7ef8
Binary files /dev/null and b/presentations/js-performance/images/event-loop.png differ
diff --git a/presentations/js-performance/images/page-speed-tools.png b/presentations/js-performance/images/page-speed-tools.png
new file mode 100644
index 0000000..026883c
Binary files /dev/null and b/presentations/js-performance/images/page-speed-tools.png differ
diff --git a/presentations/js-performance/images/perf-chart.png b/presentations/js-performance/images/perf-chart.png
new file mode 100644
index 0000000..3e48c0a
Binary files /dev/null and b/presentations/js-performance/images/perf-chart.png differ
diff --git a/presentations/js-performance/images/web-page-test.png b/presentations/js-performance/images/web-page-test.png
new file mode 100644
index 0000000..75f95d6
Binary files /dev/null and b/presentations/js-performance/images/web-page-test.png differ
diff --git a/presentations/js-performance/slideList.js b/presentations/js-performance/slideList.js
new file mode 100644
index 0000000..3c83457
--- /dev/null
+++ b/presentations/js-performance/slideList.js
@@ -0,0 +1,65 @@
+import React from 'react';
+
+import Intro from './Intro';
+import KnuthQuote from './KnuthQuote';
+import KnuthQuoteRevised from './KnuthQuoteRevised';
+import Abandonment from './Abandonment';
+import CostGuesstimate from './CostGuesstimate';
+import DaveQuote from './DaveQuote';
+import Measurement from './Measurement';
+import UserExpectation from './UserExpectation';
+import Sparkle from './Sparkle';
+import Rail from './Rail';
+import Load from './Load';
+import Idle from './Idle';
+import Response from './Response';
+import Animation from './Animation';
+import Plan from './Plan';
+import OptimizeLoad from './OptimizeLoad';
+import OptimizeNetwork from './OptimizeNetwork';
+import RemoveSequential from './RemoveSequential';
+import SequentialCode from './SequentialCode';
+import ParallelCode from './ParallelCode';
+import LazyCode from './LazyCode';
+import OptimizeResponse from './OptimizeResponse';
+import OptimizeIdle from './OptimizeIdle';
+import OptimizeFramerate from './OptimizeFramerate';
+import OptimizedApp from './OptimizedApp';
+import WebPageTest from './WebPageTest';
+import PageSpeedTools from './PageSpeedTools';
+import FrameworkSpecific from './FrameworkSpecific';
+
+const slideComponents = [
+ Intro,
+ KnuthQuote,
+ KnuthQuoteRevised,
+ Abandonment,
+ CostGuesstimate,
+ DaveQuote,
+ Measurement,
+ UserExpectation,
+ Sparkle,
+ Rail,
+ Load,
+ Idle,
+ Response,
+ Animation,
+ Plan,
+ OptimizeLoad,
+ OptimizeNetwork,
+ RemoveSequential,
+ SequentialCode,
+ ParallelCode,
+ LazyCode,
+ OptimizeResponse,
+ OptimizeIdle,
+ OptimizeFramerate,
+ OptimizedApp,
+ WebPageTest,
+ PageSpeedTools,
+ FrameworkSpecific
+];
+
+export default slideComponents.map((SlideComponent, idx) => {
+ return ;
+});
diff --git a/webpack.config.js b/webpack.config.js
index 633180b..9698210 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -47,7 +47,7 @@ module.exports = {
loaders: ["babel-loader"]
},
{
- test: /\.((gif)|(png)|(jpg)|(html)|(mp4)|(mov))$/,
+ test: /\.((gif)|(png)|(jpg)|(html)|(mp4)|(mov)|(m4a))$/,
loader: "file-loader?name=[name].[ext]"
}
]