Slide 1

Slide 1 text

Next Mobile WebApplication uupaa 2013-­11-­30

Slide 2

Slide 2 text

Me @uupaa - 2012 join R&D App

Slide 3

Slide 3 text

2013-11-30 HTML5 Conference 2013 Web 8

Slide 4

Slide 4 text

WebApp ? WebApp WebApp, Browser Game, SinglePageApplication, ES6, Storage, Cache, Audio, Canvas, WebWorker

Slide 5

Slide 5 text

NativeApp vs WebApp NativeApp WebApp ? 3D

Slide 6

Slide 6 text

WebApp

Slide 7

Slide 7 text

Single Page Application (SPA) SPA ? 1 WebApp

Slide 8

Slide 8 text

SPA (BGM) WebApp 800ms -> 200ms

Slide 9

Slide 9 text

SPA

Slide 10

Slide 10 text

Audio/WebAudio Audio BGM BGM SE Android

Slide 11

Slide 11 text

WebAudio WebAudio m4a(AAC) WebAudio SoundSprite

Slide 12

Slide 12 text

WebAudio ( ) (input) ( ) ( ) (JavaScript )

Slide 13

Slide 13 text

+-------+ +-------+ +-------+ +-------+ +-------+ | SE[0] | | SE[1] | | SE[n] | | BGM-A | | BGM-B | BufferSource <> +---+---+ +---+---+ +---+---+ +---+---+ +---+---+ | | | | | | | | +----+---+ +---+----+ | | | | FADE-A | | FADE-B | BGM CrossFade <> | | | +----+---+ +---+----+ +---------+---------+ +----+----+ | | +----+---+ +--+---+ | EFFECT | | LOOP | Effect/Loop Volume <> +----+---+ +--+---+ +-------------+------------+ +---+---+ | MUTE | Mute <> +---+---+ +------+------+ | Compressor | <> +------+------+ +------+------+ | Distination | AudioContext.Distination +-------------+

Slide 14

Slide 14 text

var ctx = new global.AudioContext(); var node = { compressor: null, fade: [null,null], // CrossFade volume master: null, // master volume effect: null, // effect volume loop: null, // loop volume mute: null // mute }; node.compressor = ctx.createDynamicsCompressor(); node.mute = ctx.createGainNode(); node.master = ctx.createGainNode(); node.effect = ctx.createGainNode(); node.loop = ctx.createGainNode(); node.fade[0] = ctx.createGainNode(); node.fade[1] = ctx.createGainNode(); node.fade[0].connect(node.loop); node.fade[1].connect(node.loop); node.effect.connect(node.master); node.loop.connect(node.master); node.master.connect(node.mute); node.mute.connect(node.compressor); node.compressor.connect(ctx.destination);

Slide 15

Slide 15 text

( MIDI)

Slide 16

Slide 16 text

WebAudio Web SE BGM OFF ON/OFF Android DualCore, 1GB RAM iOS iOS 7, iPhone 4S (iOS 6 ) Chrome for Android 31 http://hello.uupaa.net/issues/2/ HE-AAC

Slide 17

Slide 17 text

( )

Slide 18

Slide 18 text

Canvas 65 70% 200dpi (in Google Play) $179 (Moto G) 326dpi

Slide 19

Slide 19 text

Canvas CanvasRenderingContext2D#toDataURL 96dpi (ImageData) High Definition toDataURLHD toBlobHD createImageDataHD getImageDataHD putImageDataHD

Slide 20

Slide 20 text

2013 ImageData Width x Height Canvas ImageData Device 480 x 320 600 KB iPhone (2007) 960 x 640 2.3 MB iPhone Retina (2010) 2048 x 1536 12 MB iPad Retina (2012) ImageData Pixel x 4byte(RGBA)

Slide 21

Slide 21 text

2015 ImageData 2015 4K Width x Height Canvas ImageData Device 960 x 640 2.3 MB iPhone Retina (2010) 2048 x 1536 12 MB iPad Retina (2012) 2560 x 1440 14 MB 2K Android (maybe 2014) 3840 x 2160 31.6 MB 4K Android (maybe 2015)

Slide 22

Slide 22 text

ImageData ImageData MB 16ms … for

Slide 23

Slide 23 text

? WebWorker

Slide 24

Slide 24 text

WebWorker Canvas ? ? Canvas canvas.transferControlToProxy canvasProxy.setContext context.commit

Slide 25

Slide 25 text

CanvasProxy (1 ) // index.js var canvas = document.querySelector("canvas"); var canvasProxy = canvas.transferControlToProxy(); // CanvasProxy を取得 var canvasWorker = new Worker("BackgroundCanvasRender.js"); canvasWorker.postMessage(canvasProxy, [canvasProxy]); // Worker に渡す // BackgroundCanvasRender.js onmessage = function(event) { var context = new CanvasRenderingContext2D(); var canvasProxy = event.data; canvasProxy.setContext(context); // bind setInterval(function() { context.clearRect(0, 0, context.width, context.height); context.fillText(new Date() + "", 0, 100); context.commit(); // render }, 1000); };

Slide 26

Slide 26 text

? 100 150MB GC window.gc() Worker Canvas drawImage …

Slide 27

Slide 27 text

High Definition CanvasProxy + WebWorker

Slide 28

Slide 28 text

Command Pattern ( )

Slide 29

Slide 29 text

Canvas API // before ctx.fillStyle = "#fff"; ctx.fillRect(0, 0, 100, 100); // after var canvasCommands = [ ["fillStyle", "#fff"], ["fillRect", 0, 0, 100, 100] ]; for (var i = 0, iz = canvasCommands.length; i < iz; ++i) { switch (canvasCommands[i]) { case "fillStyle": ... break; case "fillRect": ... break; } });

Slide 30

Slide 30 text

: Android ( ) , ,

Slide 31

Slide 31 text

( ) Android Browser, Chrome for Android ( )

Slide 32

Slide 32 text

DrawCall ( ) Canvas API DrawCall DrawCall // before function drawCall1() { ctx.fillRect(...); } function drawCall2() { ctx.fillRect(...); } drawCall1(); heavyRoutine(); drawCall2(); Chrome Canvas profiler DrawCall CanvasAPI

Slide 33

Slide 33 text

+ SnapShot, Movie

Slide 34

Slide 34 text

+ Remote Play DOM, CSS, Audio, Canvas App

Slide 35

Slide 35 text

+ WebWorker WebWorker postMessage // index.js var worker = new Worker("Worker.js"); var request = []; worker.onmessage = function(event) { var response = event.data.response; : }; worker.postMessage({ request: request }); // Worker.js onmessage = function(event) { var request = event.data.request; postMessage({ response: ["ok"], error: null }); };

Slide 36

Slide 36 text

( )

Slide 37

Slide 37 text

WebWorker

Slide 38

Slide 38 text

WebApp WebWorker ? , , , ,

Slide 39

Slide 39 text

WebWorker JavaScript ?

Slide 40

Slide 40 text

WebWorker ( )

Slide 41

Slide 41 text

importScripts navigator.userAgent, onLine JSON, BLOB, FileReader, FileReaderSync Timer - setTimeout(), setInterval() encodeURIComponent/decodeURIComponent TypedArray MessageChannel WebSQL, WebSQLSync WebSocket, XMLHttpRequest

Slide 42

Slide 42 text

navigator.webkitPersistentStorage navigator.webkitTemporaryStorage HighPerformanceTimer - performance.now() TextEncoder/TextDecoder Crypto, Base64(atob, btoa) ImageBitmap IndexedDB WorkerConsole - console.log() RequestFileSystem, RequestFileSystemSync ES6( Symbol, Set, Map, WeakMap, WeakSet, Promise )

Slide 43

Slide 43 text

postMessage ? Nexus 7(2012) Chrome 0.6ms, Mac Chrome 0.02ms var worker = new Worker("worker.js"); var score = 0; worker.onmessage = function(event) { worker.postMessage(++score); }; setTimeout(function() { worker.postMessage("stop"); }, 10 * 1000); // worker.js var payload = []; var timerID = setInterval(function() { postMessage(payload); }, 1); onmessage = function(event) { event.data === "stop" && clearInterval(timerID); };

Slide 44

Slide 44 text

postMessage Structured Cloning var payload = new Array(big number); postMessage(payload); 1MB (payload) 32MB payload 25% var MB = 1024 * 1024; var payload = new Uint8ClampedArray( 1 * MB); // 無負荷時の 98% の速度で動作 var payload = new Uint8ClampedArray( 8 * MB); // 無負荷時の 90% の速度で動作 var payload = new Uint8ClampedArray(32 * MB); // 無負荷時の 25% の速度で動作 Structured Cloning

Slide 45

Slide 45 text

Transferable Objects postMessage (zero-copy) Transferable Objects var payload = new Array(big number); postMessage(payload, [payload]); Transferable Objects

Slide 46

Slide 46 text

WebWorker importScript JavaScript Prototype WebWorker

Slide 47

Slide 47 text

+ Module Node.js, Browser, WebWorker (function(global) { // --- define ---------------------------------------------- // --- variable -------------------------------------------- // --- interface ------------------------------------------- function Class() { } // --- implement ------------------------------------------- // --- export ---------------------------------------------- if (global.process) { module.exports = Class; } global.Class = Class; })(this.self || global); : Typical JavaScript Module Pattern

Slide 48

Slide 48 text

WebWorker ? iframe

Slide 49

Slide 49 text

WebWorker

Slide 50

Slide 50 text

Storage, Cache, Offline Storage LocalStorage 5MB … WebSQL 50MB ApplicationCache

Slide 51

Slide 51 text

LocalStorage + WebSQL Base64 DataURI

Slide 52

Slide 52 text

Asset Manifest 304 Not Modified HTTP 1 (SPDY ) 5MB + 5MB

Slide 53

Slide 53 text

Asset Manifes Sample ID URL MimeType Hash (MD5, SHA1 ) , { "HelloAssetManifest": { "url": "asset/scene/Hello.js", "hash": "fd2b04", "mime": "scene/class", "size": 1425, "prime": 1 } }

Slide 54

Slide 54 text

Storage Indexed DB + Disk Quota Management API 10% ServiceWorker URL …

Slide 55

Slide 55 text

… デモ 1 30MB (140kB jpg x 210 ) Base64 1 40 50MB 13 (600 800MB)

Slide 56

Slide 56 text

SinglePageApplication WebSQL LocalStorage Asset Manifest iframe Base64 Doubler 1 (Bulk Download)

Slide 57

Slide 57 text

Doubler Base64 ASCII SQLite(LocalStorage WebSQL ) UTF16 Doubler NULL, BOM, UTF16 Base64 200 250%

Slide 58

Slide 58 text

Doubler Code Point // Doubler.js: UTF16 Safe packer // Mobile Browser unavailable UTF16 words: // Safari: NULL, BOM // Chrome: NULL, BOM, SurrogatePairs // Android: NULL // // Desktop Browser unavailable UTF16 words: // Safari: NULL, BOM // Chrome: NULL, BOM, SurrogatePairs // // +- UINT16 -+- Doubler.pack() -+- unpack -+ // | 0x0000 | 0x0020, 0x8000 | -0x8000 | encode NULL // +----------+------------------+----------+ // | 0x0020 | 0x0020, 0x8020 | -0x8000 | encode 0x20 // +----------+------------------+----------+ // | 0xd800 | 0x0020, 0x5800 | +0x8000 | encode SurrogatePairs // | : | : | | // | 0xdfff | 0x0020, 0x5fff | | // +----------+------------------+----------+ // | 0xfffe | 0x0020, 0x7ffe | +0x8000 | encode BOM // | 0xffff | 0x0020, 0x7fff | | // +-- Tail --+- Doubler.pack() -+----------+ // | 0x00 | 0x0020, 0x9000 | -0x9000 | encode Tail byte // | : | : | | // | 0xff | 0x0020, 0x90ff | | // +----------+------------------+----------+

Slide 59

Slide 59 text

Bulk Download GIF AssetSprite 1 ? (SPDY )

Slide 60

Slide 60 text