Slide 1

Slide 1 text

Lessons on the experimental edge of web technology Remy Sharp / @rem http://www.flickr.com/photos/spaztacular/3537911581/

Slide 2

Slide 2 text

Experiment... Google IO... Head Shots.

Slide 3

Slide 3 text

Caveat

Slide 4

Slide 4 text

Two tranches of work 1. Real-time communication - WebRTC 2. Graphics and performance

Slide 5

Slide 5 text

WebRTC... aaaahhahh.

Slide 6

Slide 6 text

Don't make me write code • Peer.js • SimpleWebRTC • easyRTC • WebRTC.io

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

My Ed209

Slide 9

Slide 9 text

Three simple tasks 1. Get a room (aka pin for my game) 2. Listen and action remote connections 3. Add my video stream

Slide 10

Slide 10 text

// `pin` is the unique game id // basically a room name rtc.connect('wss:localhost:8000', pin); 1. Get a room

Slide 11

Slide 11 text

rtc.on('add remote stream', function(stream, id) { // `stream` is the video stream // 'remote' refers to a video element on the page // with the id of 'remote' rtc.attachStream(stream, 'remote'); // in the actual game we'd trigger an event to // notify that the remote stream has been added, // and we'd start playing }); 2. Listen for join

Slide 12

Slide 12 text

// this will give the video & audio stream // to any peers rtc.createStream({ 'video': true, 'audio': true }, function(stream) { // since this game doesn't show own our // face, we do nothing with the stream }); 3. Add my stream

Slide 13

Slide 13 text

The lesson Libraries are good (when you're fumbling) Libraries are bad (when shit don't work)

Slide 14

Slide 14 text

Angry cat say: WHY YOU SEND ALL TEH DATA?

Slide 15

Slide 15 text

navigator.getUserMedia({ 'video': { 'mandatory': { maxWidth: 160, maxHeight: 90 } } }, function(stream) { video.src = URL.createObjectURL(stream); // will be true when stream is running // in video elem console.log(video.videoWidth === 160); });

Slide 16

Slide 16 text

The lesson Only serve what you need. (an oldie, but a goodie)

Slide 17

Slide 17 text

It's not all client side :(

Slide 18

Slide 18 text

var express = require('express'), app = express(), server = require('http').createServer(app), webRTC = require('webrtc.io').listen(server); server.listen(8000); Bind webrtc.io. Bosh.

Slide 19

Slide 19 text

Data: RTCDataChannel 1. Reliable: true / TCP* 2. Reliable: false / UDP

Slide 20

Slide 20 text

function dataChannelOpen() { for (var id in rtc.dataChannels) { // note: string based ready states // (rather than usual ints) if (rtc.dataChannels[id].readyState == 'open') { return true; } } return false; } Test for open channel

Slide 21

Slide 21 text

Streaming shit loads of event data • If you can: avoid it • Head Shots only sends orientation event change • Peer only gets a single message

Slide 22

Slide 22 text

Mechanics 1.Open Websocket for game discovery 2.Connect video & audio stream 3.Test if data channel is "open" 4.Allow game to start...

Slide 23

Slide 23 text

How it originally worked 1. Welcome screen with join & start button 2. Start - automatically created a new game (now in waiting mode) 3. Join - prompted the user for a pin code 4. Waiting - this used a hanging XHR request 5. Play - wherein both players prompted for access to their camera

Slide 24

Slide 24 text

But...connected games would instantly disconnect. W.T.F. ?

Slide 25

Slide 25 text

Paper cup phone example

Slide 26

Slide 26 text

SPA

Slide 27

Slide 27 text

The lesson SPA can save you. SPA for persistance.

Slide 28

Slide 28 text

Add SSL

Slide 29

Slide 29 text

Graphics

Slide 30

Slide 30 text

•Started with DOM, but 3D sucked •Moved to Three.js with help from @seb_ly 3D

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

•CSS3 Renderer performed... •But caused entire chunks to vanish •Weekend later, bug fixed in Chrome Beta Fixing 3D

Slide 33

Slide 33 text

•Render background once, separately from moving foreground •Only one video (not two) •rAF, not setInterval Back to 30fps+

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

No content

Slide 36

Slide 36 text

No content

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

No content

Slide 39

Slide 39 text

No content

Slide 40

Slide 40 text

Playing in the future

Slide 41

Slide 41 text

Crazy latest tech also means latest standards. •Micro DOM & event library: min.js •Extensive use of HTML5's classList & dataset •Micro data binding: bind.js •Super minimal xhr

Slide 42

Slide 42 text

// `pin` is the unique game id var $ = document.querySelectorAll.bind(document); Node.prototype.on = Node.prototype.addEventListener; $('#somelink')[0].on('touchstart', handleTouch); Less is $$$$ https://github.com/remy/min.js

Slide 43

Slide 43 text

$('#gameover').dataset.winner = winner; $('#gameover').classList.add('showWinner'); Less is...HTML5 https://github.com/remy/min.js

Slide 44

Slide 44 text

$.on('gameReady', startTheFunk); // Later that day... if (connected) { $.trigger('gameReady'); } Less is...pubsub https://github.com/remy/min.js

Slide 45

Slide 45 text

var callback; if (typeof mapping[path.join('.')] === 'function') { callback = mapping[path.join('.')]; } else { callback = function (value) { if (callback.elements) { forEach.call(callback.elements, function (element) { element.innerHTML = value; }); } }; callback.elements = $(mapping[path.join('.')] || '☺'); } Object.defineProperty(target, key, { set: function (v) { value = v; if (callback) { callback(value); } }, get: function () { return value; } }); target[key] = value; Data binding ~40 lines

Slide 46

Slide 46 text

var callback; if (typeof mapping[path.join('.')] === 'function') { callback = mapping[path.join('.')]; } else { callback = function (value) { if (callback.elements) { forEach.call(callback.elements, function (element) { element.innerHTML = value; }); } }; callback.elements = $(mapping[path.join('.')] || '☺'); } Object.defineProperty(target, key, { set: function (v) { value = v; if (callback) { callback(value); } }, get: function () { return value; } }); target[key] = value; Setter/getter action

Slide 47

Slide 47 text

var callback; if (typeof mapping[path.join('.')] === 'function') { callback = mapping[path.join('.')]; } else { callback = function (value) { if (callback.elements) { forEach.call(callback.elements, function (element) { element.innerHTML = value; }); } }; callback.elements = $(mapping[path.join('.')] || '☺'); } Object.defineProperty(target, key, { set: function (v) { value = v; if (callback) { callback(value); } }, get: function () { return value; } }); target[key] = value; innerHTML setting

Slide 48

Slide 48 text

var callback; if (typeof mapping[path.join('.')] === 'function') { callback = mapping[path.join('.')]; } else { callback = function (value) { if (callback.elements) { forEach.call(callback.elements, function (element) { element.innerHTML = value; }); } }; callback.elements = $(mapping[path.join('.')] || '☺'); } Object.defineProperty(target, key, { set: function (v) { value = v; if (callback) { callback(value); } }, get: function () { return value; } }); target[key] = value; Initialise

Slide 49

Slide 49 text

game = new Bind({ me: { // original object score: 10 } }, { 'me.score': '#myscore' }); // DOM updates game.me.score = 20; https://github.com/remy/bind Binding

Slide 50

Slide 50 text

game = new Bind(result.data, { 'me.score': '#myscore', 'them.score': function (value) { $.trigger('theirScore', value); }, 'turn': function (myturn) { turnEl.dataset.turn = myturn; $.trigger('myturn', myturn); } }); https://github.com/remy/bind

Slide 51

Slide 51 text

The biggies •Newness of WebRTC ate way more time than I anticipated •Google engineers are there to help •Maybe the horse came before the cart?

Slide 52

Slide 52 text

Is WebRTC ready for prime time? •I'm not so sure •Support and interop is being aggressively worked on...but Microsoft...Apple? •Mobile looking hopeful •Give it 12 months* * Check expiry date

Slide 53

Slide 53 text

No content

Slide 54

Slide 54 text

http://io13webrtc.appspot.com

Slide 55

Slide 55 text

Live here: headshots.leftlogic.com Code: github.com/leftlogic/headshots Yes, I know the source is a bit of a mess! Remy Sharp / @rem Have a pop.