CREATING YOUR OWN IMAGE FORMAT IN THE BROWSER

ATWOOD’S LAW

ATWOOD’S LAW "ANY APPLICATION THAT CAN BE WRITTEN IN JAVASCRIPT, WILL EVENTUALLY BE WRITTEN IN JAVASCRIPT.”

CREATING YOUR OWN IMAGE FORMAT IN THE BROWSER

@TRENTMWILLIS

LOVE THE WEB LOVE JAVASCRIPT @TRENTMWILLIS #JSCONFCO

JAVASCRIPT CONTINUES TO EVOLVE @TRENTMWILLIS #JSCONFCO

WEB HYPERTEXT APPLICATION TECHNOLOGY WORKING GROUP @TRENTMWILLIS #JSCONFCO

WHATWG @TRENTMWILLIS #JSCONFCO

STREAMS SPEC @TRENTMWILLIS #JSCONFCO

STREAMS SPEC “IF INSTALLED INSIDE THE FETCH HOOK OF A SERVICE WORKER, THIS WOULD ALLOW DEVELOPERS TO TRANSPARENTLY POLYFILL NEW IMAGE FORMATS.” @TRENTMWILLIS #JSCONFCO

@TRENTMWILLIS #JSCONFCO

WEB CRYPTO API @TRENTMWILLIS #JSCONFCO

CRYPTOGRAM @TRENTMWILLIS #JSCONFCO

Encrypted Image @TRENTMWILLIS #JSCONFCO

Encrypted Image <img src=“./encrypted-image.epng”> @TRENTMWILLIS #JSCONFCO

Encrypted Image <img src=“./encrypted-image.epng”> *Can Display PNG/JPEG, Not EPNG @TRENTMWILLIS #JSCONFCO

Encrypted Image Service Worker <img src=“./encrypted-image.epng”> *Can Display PNG/JPEG, Not EPNG @TRENTMWILLIS #JSCONFCO

Encrypted Image .epng Service Worker .png <img src=“./encrypted-image.epng”> *Can Display PNG/JPEG, Not EPNG @TRENTMWILLIS #JSCONFCO

FETCHEVENT @TRENTMWILLIS #JSCONFCO

FETCHEVENT .RESPONDWITH() @TRENTMWILLIS #JSCONFCO

ALL CODE WILL BE AVAILABLE ONLINE @TRENTMWILLIS #JSCONFCO

navigator.serviceWorker.register(‘./service-worker.js’); @TRENTMWILLIS #JSCONFCO

self.addEventListener('fetch', (event) => { }); @TRENTMWILLIS #JSCONFCO

self.addEventListener('fetch', (event) => { if (isEncryptedImageRequest(event.request)) isEncryptedImageRequest { } }); @TRENTMWILLIS #JSCONFCO

const isEncryptedImageRequest = request => request.url.endsWith('.epng'); @TRENTMWILLIS #JSCONFCO

self.addEventListener('fetch', (event) => { if (isEncryptedImageRequest(event.request)) isEncryptedImageRequest { }; }); @TRENTMWILLIS #JSCONFCO

self.addEventListener('fetch', (event) => { if (isEncryptedImageRequest(event.request)) { event.respondWith(pngFromEncryptedImageRequest(event.request)); pngFromEncryptedImageRequest }; }); @TRENTMWILLIS #JSCONFCO

const pngFromEncryptedImageRequest = async (request) => {; }; @TRENTMWILLIS #JSCONFCO

const pngFromEncryptedImageRequest = async (request) => {; const response = await fetch(request); }; @TRENTMWILLIS #JSCONFCO

const pngFromEncryptedImageRequest = async (request) => {; const response = await fetch(request); const encryptedData = await response.arrayBuffer(); }; @TRENTMWILLIS #JSCONFCO

const pngFromEncryptedImageRequest = async (request) => {; const response = await fetch(request); const encryptedData = await response.arrayBuffer(); const decryptedData = await decryptData(encryptedData); }; @TRENTMWILLIS #JSCONFCO

const pngFromEncryptedImageRequest = async (request) => {; const const const const response = await fetch(request); encryptedData = await response.arrayBuffer(); decryptedData = await decryptData(encryptedData); pngBlob = new Blob([decryptedData], {;type: 'image/png' }); }; @TRENTMWILLIS #JSCONFCO

const pngFromEncryptedImageRequest = async (request) => {; const const const const response = await fetch(request); encryptedData = await response.arrayBuffer(); decryptedData = await decryptData(encryptedData); pngBlob = new Blob([decryptedData], {;type: 'image/png' }); return new Response(pngBlob); }; @TRENTMWILLIS #JSCONFCO

const pngFromEncryptedImageRequest = async (request) => {; const const const const response = await fetch(request); encryptedData = await response.arrayBuffer(); decryptedData = await decryptData(encryptedData); pngBlob = new Blob([decryptedData], {;type: 'image/png' }); return new Response(pngBlob); }; @TRENTMWILLIS #JSCONFCO

const pngFromCustomImageRequest = async (request, transform) => {; const const const const response = await fetch(request); rawData = await response.arrayBuffer(); pngData = await transform(rawData); pngBlob = new Blob([pngData], {;type: 'image/png' }); return new Response(pngBlob); }; @TRENTMWILLIS #JSCONFCO

WEB CRYPTO API @TRENTMWILLIS #JSCONFCO

SUBTLECRYPTO @TRENTMWILLIS #JSCONFCO

SUBTLECRYPTO “IT IS NAMED SUBTLECRYPTO TO REFLECT THE FACT THAT MANY OF THESE ALGORITHMS HAVE SUBTLE USAGE REQUIREMENTS IN ORDER TO PROVIDE THE REQUIRED ALGORITHMIC SECURITY GUARANTEES.” @TRENTMWILLIS #JSCONFCO

CRYPTOKEY @TRENTMWILLIS #JSCONFCO

const pngFromEncryptedImageRequest = async (request) => { const const const const response = await fetch(request); encryptedData = await response.arrayBuffer(); decryptedData = await decryptData decryptData(encryptedData); pngBlob = new Blob([decryptedData], { type: 'image/png' }); return new Response(pngBlob); }; @TRENTMWILLIS #JSCONFCO

const decryptData = async (encryptedData) => {; }; @TRENTMWILLIS #JSCONFCO

const decryptData = async (encryptedData) => {; const cryptoKey = await getCryptoKey(); };; @TRENTMWILLIS #JSCONFCO

const decryptData = async (encryptedData) => {; const cryptoKey = await getCryptoKey(); const decryptionOptions = {; name: ‘AES-CTR', counter: new Uint8Array(16), length: 128 }; };; @TRENTMWILLIS #JSCONFCO

const decryptData = async (encryptedData) => {; const cryptoKey = await getCryptoKey(); const decryptionOptions = {; name: ‘AES-CTR', counter: new Uint8Array(16), length: 128 }; return crypto.subtle.decrypt( decryptionOptions, cryptoKey, encryptedData ); };; @TRENTMWILLIS #JSCONFCO

const pngFromEncryptedImageRequest = async (request) => { const const const const response = await fetch(request); encryptedData = await response.arrayBuffer(); decryptedData = await decryptData(encryptedData); pngBlob = new Blob([decryptedData], { type: 'image/png' }); return new Response(pngBlob); }; @TRENTMWILLIS #JSCONFCO

cryptogram-naive.glitch.me @TRENTMWILLIS #JSCONFCO

MAKE IT WORK MAKE IT RIGHT MAKE IT FAST @TRENTMWILLIS #JSCONFCO

Encrypted Data @TRENTMWILLIS #JSCONFCO

Encrypted Data @TRENTMWILLIS Decrypted Data #JSCONFCO

BATCH Batch digra PROCESSING Encrypted Chunk Encrypted Chunk Decrypted Chunk @TRENTMWILLIS Encrypted Chunk Decrypted Chunk Encrypted Chunk Decrypted Chunk Encrypted Chunk Decrypted Chunk Encrypted Chunk Decrypted Chunk Decrypted Chunk #JSCONFCO

STREAM Batch digra PROCESSING Encrypted Chunk Encrypted Chunk Decrypted Chunk @TRENTMWILLIS Encrypted Chunk Decrypted Chunk Encrypted Chunk Decrypted Chunk Encrypted Chunk Decrypted Chunk Encrypted Chunk Decrypted Chunk Decrypted Chunk #JSCONFCO

Encrypted Chunk Encrypted Chunk Decrypted Chunk @TRENTMWILLIS Encrypted Chunk Decrypted Chunk Encrypted Chunk Decrypted Chunk Encrypted Chunk Decrypted Chunk Encrypted Chunk Decrypted Chunk Decrypted Chunk #JSCONFCO

Encrypted Chunk Encrypted Chunk Decrypted Chunk @TRENTMWILLIS Encrypted Chunk Decrypted Chunk Encrypted Chunk Decrypted Chunk Encrypted Chunk Decrypted Chunk Encrypted Chunk Decrypted Chunk Decrypted Chunk #JSCONFCO

Encrypted Chunk Encrypted Chunk Decrypted Chunk @TRENTMWILLIS Encrypted Chunk Decrypted Chunk Encrypted Chunk Decrypted Chunk Encrypted Chunk Decrypted Chunk Encrypted Chunk Decrypted Chunk Decrypted Chunk #JSCONFCO

Encrypted Chunk Encrypted Chunk Decrypted Chunk @TRENTMWILLIS Encrypted Chunk Decrypted Chunk Encrypted Chunk Decrypted Chunk Encrypted Chunk Decrypted Chunk Encrypted Chunk Decrypted Chunk Decrypted Chunk #JSCONFCO

Encrypted Data Encrypted Chunk Encrypted Chunk Decrypted Chunk Encrypted Chunk Decrypted Chunk @TRENTMWILLIS Decrypted Data Encrypted Chunk Decrypted Chunk Encrypted Chunk Decrypted Chunk Encrypted Chunk Decrypted Chunk Decrypted Chunk #JSCONFCO

const pngFromEncryptedImageRequest = async (request) => {; const const const const response = await fetch(request); encryptedData = await response.arrayBuffer(); decryptedData = await decryptData(encryptedData); pngBlob = new Blob([decryptedData], { type: 'image/png' }); return new Response(pngBlob); }; @TRENTMWILLIS #JSCONFCO

const pngFromEncryptedImageRequest = async (request) => {; const response = await fetch(request); const decrypter = new TransformStream(new Decrypter()); const pngStream = response.body.pipeThrough(decrypter); return new Response(pngStream); }; @TRENTMWILLIS #JSCONFCO

const pngFromEncryptedImageRequest = async (request) => {; const response = await fetch(request); const decrypter = new TransformStream(new Decrypter()); const pngStream = response.body.pipeThrough(decrypter); return new Response(pngStream); }; @TRENTMWILLIS #JSCONFCO

const pngFromEncryptedImageRequest = async (request) => {; const response = await fetch(request); const decrypter = new TransformStream(new Decrypter()); const pngStream = response.body.pipeThrough(decrypter); return new Response(pngStream); }; @TRENTMWILLIS #JSCONFCO

const pngFromEncryptedImageRequest = async (request) => {; const response = await fetch(request); const decrypter = new TransformStream(new Decrypter()); const pngStream = response.body.pipeThrough(decrypter); return new Response(pngStream); }; @TRENTMWILLIS #JSCONFCO

const pngFromEncryptedImageRequest = async (request) => {; const response = await fetch(request); const decrypter = new TransformStream(new Decrypter()); const pngStream = response.body.pipeThrough(decrypter); return new Response(pngStream); }; @TRENTMWILLIS #JSCONFCO

WRITABLESTREAM @TRENTMWILLIS #JSCONFCO

WRITABLESTREAM READABLESTREAM @TRENTMWILLIS #JSCONFCO

WRITABLESTREAM + READABLESTREAM = TRANSFORMSTREAM @TRENTMWILLIS #JSCONFCO

TRANSFORMER @TRENTMWILLIS #JSCONFCO

TRANSFORMER .START() .TRANSFORM() .FLUSH() @TRENTMWILLIS #JSCONFCO

const pngFromEncryptedImageRequest = async (request) => { const response = await fetch(request); const decrypter = new TransformStream(new Decrypter Decrypter()); const pngStream = response.body.pipeThrough(decrypter); return new Response(pngStream); }; @TRENTMWILLIS #JSCONFCO

class Decrypter {; async start() {; }; async transform() {; }; async flush() {; }; }; @TRENTMWILLIS #JSCONFCO

class Decrypter {; async start() {; this.counter = new Uint8Array(16); }; async transform() {; }; async flush() {; }; }; @TRENTMWILLIS #JSCONFCO

class Decrypter {; async start() {; this.counter = new Uint8Array(16); this.key = await getCryptoKey(); }; async transform() {; }; };; async flush() {; }; }; @TRENTMWILLIS #JSCONFCO

async transform(chunk, controller) {; let data = chunk; const length = data.length; const blocks = Math.floor(length / 16); const decryptedData = await decrypt(this.key, data, this.counter); controller.enqueue(decryptedData); for (let i = 0; i < blocks; i++) { incrementUint8ArrayCounter(this.counter); }; };; @TRENTMWILLIS #JSCONFCO

async transform(chunk, controller) {; let data = chunk; const length = data.length; const blocks = Math.floor(length / 16); const remainder = length % 16; if (remainder) { data = data.slice(0, length - remainder); this.remainderData = data.slice(length - remainder); }; const decryptedData = await decrypt(this.key, data, this.counter); controller.enqueue(decryptedData); for (let i = 0; i < blocks; i++) { incrementUint8ArrayCounter(this.counter); }; }; @TRENTMWILLIS #JSCONFCO

async transform( transform(chunk, controller) ) {; let data = chunk; if (this.remainderData) { data = concatUint8Arrays(this.remainderData, data); this.remainderData = null; } const length = data.length; const blocks = Math.floor(length / 16); const remainder = length % 16; if (remainder) { data = data.slice(0, length - remainder); this.remainderData = data.slice(length - remainder); }; const decryptedData = await decrypt(this.key, data, this.counter); controller.enqueue(decryptedData); for (let i = 0; i < blocks; i++) { incrementUint8ArrayCounter(this.counter); }; }; @TRENTMWILLIS #JSCONFCO

class Decrypter {; async start() {; this.counter = new Uint8Array(16); this.key = await getCryptoKey(); }; async transform() { {; // It's too long! }; async flush() {; }; }; @TRENTMWILLIS #JSCONFCO

class Decrypter {; async start() {; this.counter = new Uint8Array(16); this.key = await getCryptoKey(); }; async transform() {; // It's too long! }; async flush(controller) {; if (this.remainderData) { const decryptedData = await decrypt( this.key, this.remainderData, this.counter); controller.enqueue(decryptedData); } }; }; @TRENTMWILLIS #JSCONFCO

cryptogram-streaming.glitch.me @TRENTMWILLIS #JSCONFCO

WEBASSEMBLY FOR LIGHTNING FAST TRANSFORM STREAMS wasm-streams.glitch.me @TRENTMWILLIS #JSCONFCO

RUNTIME STREAMING COMPILATION FOR DEVELOPMENT @TRENTMWILLIS #JSCONFCO

HTML MODULE POLYFILL html-modules-polyfill.glitch.me @TRENTMWILLIS #JSCONFCO

PACKAGE NAME MAPS POLYFILL package-name-maps.glitch.me @TRENTMWILLIS #JSCONFCO

RUNTIME VIDEO EFFECTS @TRENTMWILLIS #JSCONFCO

RUNTIME STREAMING SERVICE WORKERS COMPILATION FOR DEVELOPMENT STREAMS HTML MODULE POLYFILL WEB CRYPTO PACKAGE NAME MAPS POLYFILL ESNEXT RUNTIME VIDEO EFFECTS @TRENTMWILLIS WEB ASSEMBLY WEB COMPONENTS AND MORE! #JSCONFCO

WRITE EVERYTHING IN JAVASCRIPT! * *WITHIN REASON, BUT DEFINITELY HAVE FUN! **ALSO, GLITCH IS GREAT FOR EXPERIMENTS! @TRENTMWILLIS #JSCONFCO