Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Next Mobile WebApplication
Search
uupaa
December 02, 2013
Programming
13
6.5k
Next Mobile WebApplication
このスライドは
2013-11-30 に開催された HTML5 Conference 2013 で
地下鉄・サクサク・これからのWebゲームアプリが備えるべき8つの機能
としてお話したものです
uupaa
December 02, 2013
Tweet
Share
Other Decks in Programming
See All in Programming
ファインディLT_ポケモン対戦の定量的分析
fufufukakaka
0
920
Introduction to kotlinx.rpc
arawn
0
770
PRレビューのお供にDanger
stoticdev
1
230
Jakarta EE meets AI
ivargrimstad
0
410
Generating OpenAPI schema from serializers throughout the Rails stack - Kyobashi.rb #5
envek
1
390
Rubyと自由とAIと
yotii23
6
1.8k
一休.com のログイン体験を支える技術 〜Web Components x Vue.js 活用事例と最適化について〜
atsumim
0
990
なぜイベント駆動が必要なのか - CQRS/ESで解く複雑系システムの課題 -
j5ik2o
14
4.7k
Flutter × Firebase Genkit で加速する生成 AI アプリ開発
coborinai
0
170
Ruby on cygwin 2025-02
fd0
0
180
仕様変更に耐えるための"今の"DRY原則を考える
mkmk884
9
3.2k
Datadog Workflow Automation で圧倒的価値提供
showwin
1
170
Featured
See All Featured
Six Lessons from altMBA
skipperchong
27
3.6k
The Cult of Friendly URLs
andyhume
78
6.2k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
45
9.4k
Designing on Purpose - Digital PM Summit 2013
jponch
117
7.1k
Code Review Best Practice
trishagee
67
18k
Docker and Python
trallard
44
3.3k
Practical Orchestrator
shlominoach
186
10k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
248
1.3M
Designing for humans not robots
tammielis
250
25k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
29
1k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
27
1.6k
A Tale of Four Properties
chriscoyier
158
23k
Transcript
Next Mobile WebApplication uupaa 2013-11-30
Me @uupaa - 2012 join R&D App
2013-11-30 HTML5 Conference 2013 Web 8
WebApp ? WebApp WebApp, Browser Game, SinglePageApplication, ES6, Storage, Cache,
Audio, Canvas, WebWorker
NativeApp vs WebApp NativeApp WebApp ? 3D
WebApp
Single Page Application (SPA) SPA ? 1 WebApp
SPA (BGM) WebApp 800ms -> 200ms
SPA
Audio/WebAudio Audio <audio> BGM BGM SE Android <audio>
WebAudio WebAudio m4a(AAC) WebAudio <audio> SoundSprite
WebAudio ( ) (input) ( ) ( ) (JavaScript )
+-------+ +-------+ +-------+ +-------+ +-------+ | SE[0] | | SE[1]
| | SE[n] | | BGM-A | | BGM-B | BufferSource <<AudioBuffer>> +---+---+ +---+---+ +---+---+ +---+---+ +---+---+ | | | | | | | | +----+---+ +---+----+ | | | | FADE-A | | FADE-B | BGM CrossFade <<Gain>> | | | +----+---+ +---+----+ +---------+---------+ +----+----+ | | +----+---+ +--+---+ | EFFECT | | LOOP | Effect/Loop Volume <<Gain>> +----+---+ +--+---+ +-------------+------------+ +---+---+ | MUTE | Mute <<Gain>> +---+---+ +------+------+ | Compressor | <<DynamicsCompressor>> +------+------+ +------+------+ | Distination | AudioContext.Distination +-------------+
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);
( MIDI)
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
( )
Canvas 65 70% 200dpi (in Google Play) $179 (Moto G)
326dpi
Canvas CanvasRenderingContext2D#toDataURL 96dpi (ImageData) High Definition toDataURLHD toBlobHD createImageDataHD getImageDataHD
putImageDataHD
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)
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)
ImageData ImageData MB 16ms … for
? WebWorker
WebWorker Canvas ? ? Canvas canvas.transferControlToProxy canvasProxy.setContext context.commit
CanvasProxy (1 ) <canvas></canvas> // 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); };
? 100 150MB GC window.gc() <img> <video> <svg> Worker Canvas
drawImage …
High Definition CanvasProxy + WebWorker
Command Pattern ( )
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; } });
: Android ( ) , ,
( ) Android Browser, Chrome for Android ( )
DrawCall ( ) Canvas API DrawCall DrawCall // before function
drawCall1() { ctx.fillRect(...); } function drawCall2() { ctx.fillRect(...); } drawCall1(); heavyRoutine(); drawCall2(); Chrome Canvas profiler DrawCall CanvasAPI
+ SnapShot, Movie
+ Remote Play DOM, CSS, Audio, Canvas App
+ 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 }); };
( )
WebWorker
WebApp WebWorker ? , , , ,
WebWorker JavaScript ?
WebWorker ( )
importScripts navigator.userAgent, onLine JSON, BLOB, FileReader, FileReaderSync Timer - setTimeout(),
setInterval() encodeURIComponent/decodeURIComponent TypedArray MessageChannel WebSQL, WebSQLSync WebSocket, XMLHttpRequest
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 )
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); };
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
Transferable Objects postMessage (zero-copy) Transferable Objects var payload = new
Array(big number); postMessage(payload, [payload]); Transferable Objects
WebWorker importScript JavaScript Prototype WebWorker
+ 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
WebWorker ? iframe
WebWorker
Storage, Cache, Offline Storage LocalStorage 5MB … WebSQL 50MB ApplicationCache
LocalStorage + WebSQL Base64 DataURI <img>
Asset Manifest 304 Not Modified HTTP 1 (SPDY ) 5MB
+ 5MB
Asset Manifes Sample ID URL MimeType Hash (MD5, SHA1 )
, { "HelloAssetManifest": { "url": "asset/scene/Hello.js", "hash": "fd2b04", "mime": "scene/class", "size": 1425, "prime": 1 } }
Storage Indexed DB + Disk Quota Management API 10% ServiceWorker
URL …
… デモ 1 30MB (140kB jpg x 210 ) Base64
1 40 50MB 13 (600 800MB)
SinglePageApplication WebSQL LocalStorage Asset Manifest iframe Base64 Doubler 1 (Bulk
Download)
Doubler Base64 ASCII SQLite(LocalStorage WebSQL ) UTF16 Doubler NULL, BOM,
UTF16 Base64 200 250%
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 | | // +----------+------------------+----------+
Bulk Download GIF AssetSprite 1 ? (SPDY )
</thank-you>