Senior web developer at AKQA
github.com/timbenniks
@timbenniks
The Project
Technical challenges
Pragmatic solutions
Questions
Interactive TV commercial
Choose to cross the line
Emphasis on intense moments
Animating with clips
Animating masks on moving parts
Streaming or pre-loading?
Time-based events
Async / event based
Only load what you need
Load during idle time
Garbage collect
Use a CDN with versioned content
Concat and minify JavaScript and CSS in the build
Use GZIP compression
Videos have a time-based api. We needed a frame-based api.
We needed looping and tweening over frames in time
Simple tweening, seperate frame images, canvas
var toFframe = 100,
callback = function() { alert('What would Chuck Norris do?'); };
tweenToFrame(toFframe, callback);
// {Number} toFrame
// {Function} callback
tweenToFrame = function(toFrame, callback)
{
// object with property to tween
var sequenceData = { frame: currentFrame },
time = 500; // time in ms
tween.to(sequenceData, time,
{
value: toFrame,
onUpdate: function()
{
showFrame(~~sequenceData.frame);
},
onComplete: function()
{
if(callback) callback();
}
});
}
// List of frames (created by the pre-loader)
var frames =
[
{ url: 'image.jpg', tag: 'HTMLIMAGEElement' },
{ url: 'image2.jpg', tag: 'HTMLIMAGEElement' }
],
showFrame = function (frameNo)
{
context.drawImage(
frames[frameNo].tag,
0, 0, 1100, 618, 0, 0, 1100, 618
);
}
var pathPoints =
{
topLeftX: 86,
topLeftY: 0,
topRightX: 593,
topRightY: 0,
bottomRightX: 507,
bottomRightY: 618,
bottomLeftX: 0,
bottomLeftY: 618
};
// Automatically updates pathPoints object with tween
Tween.to(pathPoints, time,
{
topRightX: 454,
bottomRightX: 368,
ease: 'Expo.EaseInOut',
onUpdate: function()
{
// currentFrame is a global var set by the
// mouseover function which plays the sequence
showFrame(currentFrame);
}
});
showFrame = function (frame)
{
context.save();
context.clearRect(0, 0, 1100, 618);
context.beginPath();
context.moveTo(pathPoints.topLeftX, pathPoints.topLeftY);
context.lineTo(pathPoints.topRightX, pathPoints.topRightY);
context.lineTo(pathPoints.bottomRightX, pathPoints.bottomRightY);
context.lineTo(pathPoints.bottomLeftX, pathPoints.bottomLeftY);
context.lineTo(pathPoints.topLeftX, pathPoints.topLeftY);
context.closePath();
context.clip();
context.drawImage(frames[frame].tag, 0, 0, 1100, 618, 0, 0, 1100, 618);
context.restore();
}
Pre-loading a video only sometimes works
No matter how you encode it
It also depends on how it's served from the CDN
I think...
Obviously it worked fine on my machine
Server sends chunks of film with 206 partial content header
It's more stable than pre-loading
Just whack the video tag into the DOM and be happy
We needed subtitles, timed accolades, sound triggers, analytics triggers
cuePoint(videotag, 2.1, function() { triggerEvent('sound::play', 'engine1'); });
cuePoint(videotag, 2.4, function() { triggerEvent('sound::stop', 'engine1'); });
Output screenshots from the game
360 cameras around an intense moment
Render in CANVAS
Tween through frames by clicking on a hotspot
Information overlay is another mask animation with canvas
Also for movies
Partner for sound design and JS library
Their JS library played the sounds via our events system
These guys rock
This is only half of them though
I'm off to Paris next week
If you want my job, come chat to us...