GENERATE ART EVERYWHERE ) s t n e n o p m o c b e (with w AND(wFAST! ith web workers) @trentmwillis | #NEJSConf

Have you ever been a part of an online forum community?

JavaScript + Art

JavaScript + Art = Fun

Too often art stands alone.

Too often art stands alone. What if we embedded it in existing user experiences?

! n e p p a h s i h t e k a m o t g n i o g e r We’ <generate-art></generate-art>

GENERATE ART EVERYWHERE ) s t n e n o p m o c b e (with w AND(wFAST! ith web workers) @trentmwillis | #NEJSConf

What type of art should we create with JavaScript? @trentmwillis | #NEJSConf

What type of art should we create with JavaScript? Generative Art @trentmwillis | #NEJSConf

Generative Art is created by “non-human systems” Input Generated Art System @trentmwillis | #NEJSConf

Generative Computer == Art Art @trentmwillis | #NEJSConf

Generative Computer !== Art Art @trentmwillis | #NEJSConf

Not strictly the same thing. But loosely the same. Generative Computer !== Art Art @trentmwillis | #NEJSConf

Generative Art Algorithmic Robotic Biological Computer Art Art Chemical Art Art Art @trentmwillis | #NEJSConf

Algorithmic Computer Art @trentmwillis | #NEJSConf

@trentmwillis | #NEJSConf

JavaScript is good for Algorithmic Computer Art @trentmwillis | #NEJSConf

There are two ways to approach art in JavaScript Manual Approach Library Approach <canvas />, Web APIs p5.js, d3.js, chromata @trentmwillis | #NEJSConf

Use both approac hes! <canvas /> + p5.js @trentmwillis | #NEJSConf

@trentmwillis | #NEJSConf

glitch.com/~nejs-demo-1 @trentmwillis | #NEJSConf

<canvas id=”canvas”></canvas> @trentmwillis | #NEJSConf

<canvas id=”canvas”></canvas> <script src=”./generate-art.js”></script> @trentmwillis | #NEJSConf

// generate-art.js const canvas = document.getElementById(‘canvas’); setup(canvas); setup render(canvas); @trentmwillis | #NEJSConf

const setup = (canvas) => { }; @trentmwillis | #NEJSConf

const setup = (canvas) => { canvas.width = 500; canvas.height = 280; }; @trentmwillis | #NEJSConf

// generate-art.js const canvas = document.getElementById(‘canvas’); setup(canvas); render(canvas); @trentmwillis | #NEJSConf

const render = (canvas) => { }; @trentmwillis | #NEJSConf

const render = (canvas) => { const drawingContext = canvas.getContext(‘2d’); }; @trentmwillis | #NEJSConf

const render = (canvas) => { const drawingContext = canvas.getContext(‘2d’); const seedValue = 9; const randomNumberGenerator = pseudoRandomNumberGenerator(seedValue); };; @trentmwillis | #NEJSConf

const render = (canvas) => { const drawingContext = canvas.getContext(‘2d’); const seedValue = 9; const randomNumberGenerator = pseudoRandomNumberGenerator(seedValue); const drawNextPoint = () => { // Draw a point using the randomNumberGenerator on the drawingContext }; };; @trentmwillis | #NEJSConf

const render = (canvas) => { const drawingContext = canvas.getContext(‘2d’); const seedValue = 9; const randomNumberGenerator = pseudoRandomNumberGenerator(seedValue); const drawNextPoint = () => { // Draw a point using the randomNumberGenerator on the drawingContext }; drawNextPoint(); }; @trentmwillis | #NEJSConf

const render = (canvas) => { const drawingContext = canvas.getContext(‘2d’); const seedValue = 9; const randomNumberGenerator = pseudoRandomNumberGenerator(seedValue); const drawNextPoint = () => { // Draw a point using the randomNumberGenerator on the drawingContext requestAnimationFrame(drawNextPoint); }; drawNextPoint(); }; @trentmwillis | #NEJSConf

How do we generate something that looks nice? @trentmwillis | #NEJSConf

How do we generate something that looks nice? Artistic Principles @trentmwillis | #NEJSConf

Artistic Principles help us create pleasing art Balance Contrast Emphasis Movement Proportion Repetition Variety @trentmwillis | #NEJSConf

M O V E M E N T @trentmwillis | #NEJSConf

repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition repetition @trentmwillis | #NEJSConf

Variety Variety Variety Variety Variety @trentmwillis | #NEJSConf

Artistic principles help us create pleasing art Balance Contrast Emphasis Movement Proportion Repetition Variety @trentmwillis | #NEJSConf

Artistic principles help us create pleasing art Balance Contrast Emphasis Movement Proportion Repetition Variety Response @trentmwillis | #NEJSConf

Responsive Art @trentmwillis | #NEJSConf

@trentmwillis | #NEJSConf

glitch.com/~nejs-demo-2 @trentmwillis | #NEJSConf

p5 + p5.sound ———————————— github.com/processing/p5.js @trentmwillis | #NEJSConf

const microphone = new p5.AudioIn(); @trentmwillis | #NEJSConf

const microphone = new p5.AudioIn(); microphone.start(() => { }); @trentmwillis | #NEJSConf

const microphone = new p5.AudioIn(); microphone.start(() => { const seed = microphone.getLevel(); }); @trentmwillis | #NEJSConf

const microphone = new p5.AudioIn(); microphone.start(() => { const seed = microphone.getLevel(); render(canvas, seed); }); @trentmwillis | #NEJSConf

n a h t r e i s a e h c u m So . I P A o i d u A b e W with the const microphone = new p5.AudioIn(); microphone.start(() => { const seed = microphone.getLevel(); render(canvas, seed); }); @trentmwillis | #NEJSConf

@trentmwillis | #NEJSConf

@trentmwillis | #NEJSConf

<style> .container { position: relative; display: flex; margin: 0; align-items: center; justify-content: center; font-family: ‘Nunito’, sans-serif; background: rgba(245, 247, 250, 1); } #start { position: absolute; color: rgba(245, 247, 250, 1); font-size: 1.5rem; background: rgba(135, 25, 224, 1); border-radius: 4px; border: none; padding: 0.5rem 1rem; cursor: pointer; transition: opacity 1s, background 0.3s, transform 0.1s; } #start:hover { background: rgba(207, 17, 36, 1); } #start:active { transform: scale(0.95); } #canvas { background: black; transition: opacity 5s; opacity: 1; } #notification { position: absolute; color: rgba(135, 25, 224, 1); font-size: 2rem; animation: pulse 2s infinite; transition: opacity 5s; max-width: 500px; text-align: center; } .is-hidden { opacity: 0 !important; pointer-events: none; } @keyframes pulse 0% { text-shadow: } 90% { text-shadow: } 100% { text-shadow: } } </style>

{ f o t o l a s ’ t ! a e h g a T m i e n o r o f e d co 0 0 0 rgba(135, 25, 224, 1), 0 0 0 rgba(135, 25, 224, 1); 0 0 2em rgba(135, 25, 224, 0), 0 0 1em rgba(135, 25, 224, 0); 0 0 0 rgba(135, 25, 224, 0), 0 0 0 rgba(135, 25, 224, 0); <canvas id=”canvas” class=”is-hidden”></canvas> <div id=”notification” class=”is-hidden”>Sampling noise for art generation</div> <button id=”start”>Generate Art</button> @trentmwillis | #NEJSConf

. e d o c f o t n u o m a e l b a n o s a e r a s ’ t a h T <generate-art width=”750” height=”250”></generate-art> @trentmwillis | #NEJSConf

Web Components let us encapsulate all that markup and presentation <generate-art width=”750” height=”250”></generate-art> @trentmwillis | #NEJSConf

Web Components make it easy to reuse your art without much extra code <generate-art width=”750” height=”250”></generate-art> <script src=”./generate-art-component.js”></script> @trentmwillis | #NEJSConf

@trentmwillis | #NEJSConf

@trentmwillis | #NEJSConf

class GenerateArt extends HTMLElement { }~ @trentmwillis | #NEJSConf

class GenerateArt extends HTMLElement { constructor() { super(); }; }~ @trentmwillis | #NEJSConf

class GenerateArt extends HTMLElement { constructor() { super(); const shadowDOM = this.attachShadow({mode: ‘closed’}); }; }~ @trentmwillis | #NEJSConf

class GenerateArt extends HTMLElement { constructor() { super(); const shadowDOM = this.attachShadow({mode: ‘closed’}); shadowDOM.innerHTML = markupForGenerateArtComponent; }; }~ @trentmwillis | #NEJSConf

class GenerateArt extends HTMLElement { constructor() { super(); const shadowDOM = this.attachShadow({mode: ‘closed’}); shadowDOM.innerHTML = markupForGenerateArtComponent; this.setDimensions( shadowDOM, this.getAttribute(‘width’), this.getAttribute(‘height’), ); }; }~ @trentmwillis | #NEJSConf

class GenerateArt extends HTMLElement { constructor() { super(); const shadowDOM = this.attachShadow({mode: ‘closed’}); shadowDOM.innerHTML = markupForGenerateArtComponent; this.setDimensions( shadowDOM, this.getAttribute(‘width’), this.getAttribute(‘height’), ); this.setupGenerateArtButton(shadowDOM); }; }~ @trentmwillis | #NEJSConf

class GenerateArt extends HTMLElement { constructor() { super(); const shadowDOM = this.attachShadow({mode: ‘closed’}); shadowDOM.innerHTML = markupForGenerateArtComponent; this.setDimensions( shadowDOM, this.getAttribute(‘width’), this.getAttribute(‘height’), ); this.setupGenerateArtButton(shadowDOM); }; }~ customElements.define(‘generate-art’, GenerateArt); @trentmwillis | #NEJSConf

@trentmwillis | #NEJSConf

glitch.com/~nejs-demo-3 @trentmwillis | #NEJSConf

@trentmwillis | #NEJSConf

Primum non nocere @trentmwillis | #NEJSConf

Primum non nocere ———————————— First, do no harm. @trentmwillis | #NEJSConf

Jank

Harm @trentmwillis | #NEJSConf

Jank happens when your code prevents a user’s action from being processed Click Render Action @trentmwillis | #NEJSConf

Use Web Workers to reduce jank Without Web Workers Main Click Render Action Worker With Web Workers Main Click Action Render @trentmwillis | #NEJSConf

Use OffscreenCanvas with Web Workers to reduce jank in your art Regular Canvas Main Thread @trentmwillis | #NEJSConf

Use OffscreenCanvas with Web Workers to reduce jank in your art Regular Canvas .transferControlToOffscreen() Offscreen Canvas Main Thread @trentmwillis | #NEJSConf

Use OffscreenCanvas with Web Workers to reduce jank in your art Regular Canvas .transferControlToOffscreen() Offscreen Canvas .postMessage() Main Thread Web Worker @trentmwillis | #NEJSConf

const canvas = shadowDOM.getElementById(‘canvas’); @trentmwillis | #NEJSConf

const canvas = shadowDOM.getElementById(‘canvas’); const offscreenCanvas = canvas.transferControlToOffscreen(); @trentmwillis | #NEJSConf

const canvas = shadowDOM.getElementById(‘canvas’); const offscreenCanvas = canvas.transferControlToOffscreen(); const worker = new Worker(‘render-worker.js’); @trentmwillis | #NEJSConf

const canvas = shadowDOM.getElementById(‘canvas’); const offscreenCanvas = canvas.transferControlToOffscreen(); const worker = new Worker(‘render-worker.js’); worker.postMessage({ canvas: offscreenCanvas, seed: sampledValue }, [offscreenCanvas]); @trentmwillis | #NEJSConf

// render-worker.js onmessage = (event) => { }; @trentmwillis | #NEJSConf

// render-worker.js onmessage = (event) => { render(event.data.canvas, event.data.seed); }; @trentmwillis | #NEJSConf

@trentmwillis | #NEJSConf

glitch.com/~nejs-demo-4 @trentmwillis | #NEJSConf

! p u t e s g n i r o b e h t p i k S ez-offscreen-canvas ———————————— github.com/trentmwillis/ez-offscreen-canvas @trentmwillis | #NEJSConf

const canvas = document.querySelector(‘canvas’); @trentmwillis | #NEJSConf

const canvas = document.querySelector(‘canvas’); const seed = getRandomNumber(); @trentmwillis | #NEJSConf

const canvas = document.querySelector(‘canvas’); const seed = getRandomNumber(); const worker = ezOffscreenCanvas( canvas, { seed }, ({ canvas, seed }) => { // Rendering logic to run in Web Worker // using OffscreenCanvas }; );; @trentmwillis | #NEJSConf

const canvas = document.querySelector(‘canvas’); const seed = getRandomNumber(); const worker = ezOffscreenCanvas( canvas, { seed }, ({ canvas, seed }) => { // Rendering logic to run in Web Worker // using OffscreenCanvas }; );; // …some time later… worker.terminate(); @trentmwillis | #NEJSConf

We started here… Generative Art Web Component Web Worker OffscreenCanvas d e d n e d n a … here! @trentmwillis | #NEJSConf

The Web is a great and practical creative outlet @trentmwillis | #NEJSConf

! s i h t x i m re glitch.com/~offscreen-canvas-kit @trentmwillis | #NEJSConf