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
わたしの星のままで一番星になる ~ 出産を機にSIerからEC事業会社に転職した話 ~
kimura_m_29
0
180
Security_for_introducing_eBPF
kentatada
0
110
DevFest Tokyo 2025 - Flutter のアプリアーキテクチャ現在地点
wasabeef
5
900
fs2-io を試してたらバグを見つけて直した話
chencmd
0
220
数十万行のプロジェクトを Scala 2から3に完全移行した
xuwei_k
0
260
今年一番支援させていただいたのは認証系サービスでした
satoshi256kbyte
1
250
競技プログラミングへのお誘い@阪大BOOSTセミナー
kotamanegi
0
350
ゆるやかにgolangci-lintのルールを強くする / Kyoto.go #56
utgwkk
1
350
アクターシステムに頼らずEvent Sourcingする方法について
j5ik2o
4
170
Cloudflare MCP ServerでClaude Desktop からWeb APIを構築
kutakutat
1
530
CSC305 Lecture 25
javiergs
PRO
0
130
開発者とQAの越境で自動テストが増える開発プロセスを実現する
92thunder
1
180
Featured
See All Featured
Testing 201, or: Great Expectations
jmmastey
40
7.1k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
32
2.7k
Keith and Marios Guide to Fast Websites
keithpitt
410
22k
Typedesign – Prime Four
hannesfritz
40
2.4k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
656
59k
Git: the NoSQL Database
bkeepers
PRO
427
64k
Art, The Web, and Tiny UX
lynnandtonic
298
20k
Agile that works and the tools we love
rasmusluckow
328
21k
Building Adaptive Systems
keathley
38
2.3k
Put a Button on it: Removing Barriers to Going Fast.
kastner
59
3.6k
Reflections from 52 weeks, 52 projects
jeffersonlam
347
20k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
0
94
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>