14 juin 2014

Some bubbles with Famo.us's physic engine in CoffeeScript and Meteor.js

Introduction

A week ago, Raix, the author of Famono package used for integrating Require.js and Famo.us with Meteor.js, has published a new release that simplifies greatly the integration.

I was willing to demonstrate just that when a nice demo of Famo.us's physic engine has shown up on my radar: http://hbsand.com/HappyBoxes/. So why not demonstrating both?


You can access the live demo here: http://famousbubble.meteor.com/.
You can also clone the code repository from Github: https://github.com/PEM--/famousbubble

Note for Chrome users on OSX : The current release of Chrome, the 36, has a bug that stops the demo running after ~1-2s. This bug is fixed in Chrome Canary version 37. I didn't see it on the current Chrome for Android or iOS. Firefox and Safari work well on OSX.

Preparing your development environment

I suppose that you have already install Node.jsMeteor.js and Meteorite on your system. 

So, lets get down to business. The first step is to create the app:
mrt create FamousBubble
cd FamousBubble
rm -rf FamousBubble.*
mkdir -p client/stylesheets client/startup client/views
mrt add coffeescript
mrt add stylus
mrt add jade
mrt add famono

For the imported Require.js package, I keep it at the bare minimum. Famono places the imported packages from a file called client/lib/smart.require:
{
  "famous": {
    "git": "https://github.com/Famous/famous.git"
  },
  "famousPolyfills": {
    "git": "https://github.com/Famous/polyfills.git"
  }
}
This has not changed from the early version.

Linking Famo.us with Meteor.js

In Famono, what has changed is the way you require your dependencies. The client/startup/famous.coffee is greatly reduced:
# This is equivalent to: require 'famousPolyfills'
famousPolyfills
# Identically, this is equivalent to: require 'famous.core.famous'
famous.core.famous
# Declaring the main context
window.mainCtx = famous.core.Engine.createContext()

Everything is now imported into the main context of Meteor.js. There is no more boilerplate code.

Our main Jade file, client/index.jade stays unchanged:
head
  title Famo.us - Physic demo
  meta(name='viewport', content='width=device-width, maximum-scale=1, user-scalable=no')
  meta(name='mobile-web-app-capable', content='yes')
  meta(name='apple-mobile-web-app-capable', content='yes')
  meta(name='apple-mobile-web-app-status-bar-style', content='black')

body
  +index

template(name='index')

The Styus file, client/stylesheets/app.styl, is also unchanged:
@import nib

html
  font-family: Helvetica, Arial, sans-serif

*
  -webkit-user-drag: none

body
  -webkit-user-callout: none
  user-select: none
  background-color: #FF851B

Like the former file, the associated CoffeeScript file client/index.coffee to the index template is almost unchanged either:
Template.index.rendered = ->
  $(document).ready ->
    window.appView = new AppView()
    mainCtx.add appView
    appView.addDragger()
    appView.addBubbles()

Easier subclassing

As the Famo.us's University told us, you create your views by inheriting from Famo.us's ones. With Famono, it is now simplified. Here the code for the AppView, client/views/AppView.coffee that we just call in the previous file, the index template:
class @AppView extends famous.core.View
  DEFAULT_OPTIONS:
    numBodies: 10
    gravity: [0, 0.0015, 0]
  constructor: (@options)->
    @constructor.DEFAULT_OPTIONS = @DEFAULT_OPTIONS
    super @options
    surf = new famous.core.Surface
      size: [400, 400]
      properties:
        backgroundColor: '#FFDC00'
        borderRadius: '8px'
    mod = new famous.core.Modifier
      origin: [.5, .5]
    @add(mod).add surf
    @gravity = new famous.physics.forces.Force @options.gravity
    @ceiling = new famous.physics.constraints.Wall
      normal: [0, 1, 0]
      distance: 200
      restitution: 0
    @floor = new famous.physics.constraints.Wall
      normal: [0, -1, 0]
      distance: 200
      restitution: 0
    @left = new famous.physics.constraints.Wall
      normal: [1, 0, 0]
      distance: 200
      restitution: 0
    @right = new famous.physics.constraints.Wall
      normal: [-1, 0, 0]
      distance: 200
      restitution: 0
    @pe = new famous.physics.PhysicsEngine()
    @collision = new famous.physics.constraints.Collision restitution: 0
    @bubbleBodies = []
    famous.inputs.GenericSync.register
      'mouse': famous.inputs.MouseSync
      'touch': famous.inputs.TouchSync
  addDragger: ->
    @dragger = new Dragger()
    @pe.addBody @dragger.body
    (@_add @dragger.state).add @dragger.shape
    sync = new famous.inputs.GenericSync ['mouse', 'touch']
    @dragger.shape.pipe sync
    sync.on 'update', (data) =>
      @dragger.position[0] += data.delta[0]
      @dragger.position[1] += data.delta[1]
      @dragger.body.setPosition @dragger.position

  addBubble: (i) =>
    bubble = new Bubble()
    @pe.addBody bubble.body
    bubble.state.transformFrom =>
      @gravity.applyForce bubble.body
      bubble.body.getTransform()
    (@add bubble.state).add bubble.shape
    @pe.attach [@right, @left, @floor, @ceiling], bubble.body
    (@pe.attach @collision, @bubbleBodies, bubble.body) if i > 0
    @pe.attach @collision, [bubble.body], @dragger.body
    @bubbleBodies.push bubble.body

  addBubbles: ->
    [0...@options.numBodies].map (i) =>
      famous.utilities.Timer.setTimeout (@addBubble.bind @, i), 1000
As you can see it, the inheritance from a Famo.us's View does not require you to wait for Famo.us to be loaded in your document. Additionally, the require step has been greatly simplified. Calling any class provided by Famo.us is done with a simple namespaced call.

I've create 2 more classes. The first one, client/views/Dragger.coffee concerns the little dragging bubble used to play with the blue bubbles:
class @Dragger
  RADIUS: 30
  constructor: ->
    @shape = new famous.core.Surface
      size: [2 * @RADIUS, 2 * @RADIUS]
      properties:
        border: '2px solid #FF4136'
        borderRadius: "#{2 * @RADIUS}px"
        backgroundColor: 'rgba(255, 255, 255, 0.5)'
    @body = new famous.physics.bodies.Circle
      radius: @RADIUS
      mass: 5
    @position = [0, 0]
    @state = new famous.core.Modifier
      origin: [.5, .5]
      transform: =>
        famous.core.Transform.translate @position[0], @position[1], 0

The second one, client/view/Bubble.coffee concerns the bubble themselves:
class @Bubble
  constructor: ->
    radius = famous.math.Random.integer 20, 60
    @shape = new famous.core.Surface
      size: [radius * 2, radius * 2]
      properties:
        backgroundColor: '#7FDBFF'
        border: '3px solid #0074D9'
        borderRadius: "#{radius}px"
    @body = new famous.physics.bodies.Circle radius: radius, mass: 2
    @state = new famous.core.Modifier origin: [.5, .5]

Conclusion

With the new Famono release, integrating Famo.us and Meteor.js is simpler than ever. My little sample is missing few things. A better separation of the controller from the views would be great. But, there is some upcoming Famo.us lessons that should arrive in the Famo.us's University. Therefore, they will surely cover their best practices. No rush.

9 juin 2014

Timbre demo from Famo.us University using Meteor.js and CoffeeScript

Introduction

As already demonstrated in my previous article Famo.us polaroid tutorial in CoffeeScript and within MeteorMeteor, CoffeeScript and Famo.us are incredibly useful tools for front end or full stack developers. This article just show the same principles with the Famo.us University Timbre tutorial. A live demo is deployed on Meteor's testing ground: http://famoustimbre.meteor.com/.

Show me the code, please

If you find yourself stuck at translating some JS concept to CoffeeScript while playing the nice tutorial from Famo.us University, I provide my code hereafter:
client/stylesheets/app.styl
@import nib

html
  font-family: Helvetica

*
  -webkit-user-drag: none

body
  -webkit-touch-callout: none
  user-select: none

client/index.jade
head
  title Famo.us Timbre
  meta(name='viewport', content='width=device-width, maximum-scale=1, user-scalable=no')
  meta(name='mobile-web-app-capable', content='yes')
  meta(name='apple-mobile-web-app-capable', content='yes')
  meta(name='apple-mobile-web-app-status-bar-style', content='black')
body
  +index

template(name='index')

client/lib/famous.coffee
window.Famous ?= {}

client/lib/timbre.coffee
window.Timbre ?= {}

client/startup/famous.coffee
# Import Famous
require 'famous/core/famous'
# Adds the famo.us dependencies
require 'famous-polyfills'
# Wait for document ready
$(document).ready ->
  # Load Famo.us libraries
  Famous.Engine           = require 'famous/core/Engine'
  Famous.Surface          = require 'famous/core/Surface'
  Famous.Transform        = require 'famous/core/Transform'
  Famous.View             = require 'famous/core/View'
  Famous.Modifier         = require 'famous/core/Modifier'
  Famous.StateModifier    = require 'famous/modifiers/StateModifier'
  Famous.HeaderFooter     = require 'famous/views/HeaderFooterLayout'
  Famous.ImageSurface     = require 'famous/surfaces/ImageSurface'
  Famous.FastClick        = require 'famous/inputs/FastClick'
  Famous.GenericSync      = require 'famous/inputs/GenericSync'
  Famous.MouseSync        = require 'famous/inputs/MouseSync'
  Famous.TouchSync        = require 'famous/inputs/TouchSync'
  Famous.GenericSync.register
    'mouse': Famous.MouseSync
    'touch': Famous.TouchSync
  Famous.Easing           = require 'famous/transitions/Easing'
  Famous.Transitionable   = require 'famous/transitions/Transitionable'
  Famous.Timer            = require 'famous/utilities/Timer'
  # Create main context
  Timbre.mainCtx = Famous.Engine.createContext()

client/models/StripData.coffee
Timbre.StripData = [
  {title: 'search', iconUrl: 'img/strip-icons/famous.png'}
  {title: 'starred', iconUrl: 'img/strip-icons/starred.png'}
  {title: 'friends', iconUrl: 'img/strip-icons/friends.png'}
  {title: 'settings', iconUrl: 'img/strip-icons/settings.png'}
]

client/index.coffee
ASPECT_RATIO = 320 / 548

Template.index.rendered = ->
  $document = $ document
  $document.ready ->
    docwidth = $document.width()
    docheight = $document.height()
    if docwidth / ASPECT_RATIO > docheight
      screenwidth = docheight * ASPECT_RATIO
      screenheight = docheight
    else
      screenwidth = docwidth
      screenheight = docwidth / ASPECT_RATIO
    appView = new Timbre.AppView()
    mainMod = new Famous.Modifier
      size: [screenwidth, screenheight]
    Timbre.mainCtx
      .add mainMod
      .add appView

client/models/AppView.coffee
$(document).ready ->
  class Timbre.AppView extends Famous.View
    DEFAULT_OPTIONS:
      openPosition: 276
      transition:
        duration: 300
        curve: Famous.Easing.inOutBack
      posThreshold: 138
      velTreshold: 0.75
    constructor: (@options) ->
      @constructor.DEFAULT_OPTIONS = @DEFAULT_OPTIONS
      super @options
      @menuToggle = false
      @pageViewPos = new Famous.Transitionable 0
      @createPageView()
      @createMenuView()
      @setListeners()
      @handleSwipe()
    createPageView: ->
      @pageView = new Timbre.PageView()
      @pageModifier = new Famous.Modifier
        transform: =>
          Famous.Transform.translate @pageViewPos.get(), 0, 0
      @add(@pageModifier).add @pageView
    createMenuView: ->
      @menuView = new Timbre.MenuView stripData: Timbre.StripData
      menuModifier = new Famous.StateModifier
        transform: Famous.Transform.behind
      @add(menuModifier).add @menuView
    setListeners: ->
      @pageView.on 'menuToggle', @toggleMenu
    toggleMenu: =>
      if @menuToggle
        @slideLeft()
      else
        @slideRight()
        @menuView.animateStrips()
      @menuToggle = !@menuToggle
    slideLeft: ->
      @pageViewPos.set 0, @options.transition, =>
        @menuToggle = false
    slideRight: ->
      @pageViewPos.set @options.openPosition, @options.transition, =>
        @menuToggle = true
    handleSwipe: ->
      sync = new Famous.GenericSync(
        ['mouse', 'touch']
      , {direction: Famous.GenericSync.DIRECTION_X}
      )
      @pageView.pipe sync
      sync.on 'update', (data) =>
        currentPosition = @pageViewPos.get()
        @pageViewPos.set Math.max 0, currentPosition + data.delta
        if currentPosition is 0 and data.velocity > 0
          @menuView.animateStrips()
      sync.on 'end', (data) =>
        velocity = data.velocity
        position = @pageViewPos.get()
        if position > @options.posThreshold
          if velocity < -@options.velTreshold
            @slideLeft()
          else
            @slideRight()
        else
          if velocity > @options.velTreshold
            @slideRight()
          else
            @slideLeft()

client/models/PageView.coffee
$(document).ready ->
  class Timbre.PageView extends Famous.View
    DEFAULT_OPTIONS:
      headerSize: 44
    constructor: (@options) ->
      @constructor.DEFAULT_OPTIONS = @DEFAULT_OPTIONS
      super @options
      @createLayout()
      @createHeader()
      @createBody()
      @setListeners()
    createLayout: ->
      @layout = new Famous.HeaderFooter
        headerSize: @options.headerSize
      layoutModifier = new Famous.StateModifier
        transform: Famous.Transform.translate 0, 0, .1
      @add(layoutModifier).add @layout
    createHeader: ->
      backgroundSurface = new Famous.Surface
        properties: backgroundColor: 'black'
      backgroundModifier = new Famous.StateModifier
        transform: Famous.Transform.behind
      @layout.header
        .add backgroundModifier
        .add backgroundSurface
      @hamburgerSurface = new Famous.ImageSurface
        size: [44, 44]
        content: 'img/hamburger.png'
      searchSurface = new Famous.ImageSurface
        size: [232, 44]
        content: 'img/search.png'
      iconSurface = new Famous.ImageSurface
        size: [44, 44]
        content: 'img/icon.png'
      hamburgerModifier = new Famous.StateModifier
        origin: [0, .5]
        align: [0, .5]
      searchModifier = new Famous.StateModifier
        origin: [.5, .5]
        align: [.5, .5]
      iconModifier = new Famous.StateModifier
        origin: [1, .5]
        align: [1, .5]
      @layout.header
        .add hamburgerModifier
        .add @hamburgerSurface
      @layout.header
        .add searchModifier
        .add searchSurface
      @layout.header
        .add iconModifier
        .add iconSurface
    createBody: ->
      @bodySurface = new Famous.ImageSurface
        size: [undefined, true]
        content: 'img/body.png'
      @layout.content.add @bodySurface
    setListeners: ->
      @hamburgerSurface.on 'click', =>
        @_eventOutput.emit 'menuToggle'
      @bodySurface.pipe @_eventOutput
    createBacking: ->
      backing = new Famous.Surface
        properties:
          backgroundColor: 'black'
          boxShadow: '0 0 20px rgba(0,0,0,0.5)'
      @add backing

client/models/MenuView.coffee
$(document).ready ->
  class Timbre.PageView extends Famous.View
    DEFAULT_OPTIONS:
      headerSize: 44
    constructor: (@options) ->
      @constructor.DEFAULT_OPTIONS = @DEFAULT_OPTIONS
      super @options
      @createLayout()
      @createHeader()
      @createBody()
      @setListeners()
    createLayout: ->
      @layout = new Famous.HeaderFooter
        headerSize: @options.headerSize
      layoutModifier = new Famous.StateModifier
        transform: Famous.Transform.translate 0, 0, .1
      @add(layoutModifier).add @layout
    createHeader: ->
      backgroundSurface = new Famous.Surface
        properties: backgroundColor: 'black'
      backgroundModifier = new Famous.StateModifier
        transform: Famous.Transform.behind
      @layout.header
        .add backgroundModifier
        .add backgroundSurface
      @hamburgerSurface = new Famous.ImageSurface
        size: [44, 44]
        content: 'img/hamburger.png'
      searchSurface = new Famous.ImageSurface
        size: [232, 44]
        content: 'img/search.png'
      iconSurface = new Famous.ImageSurface
        size: [44, 44]
        content: 'img/icon.png'
      hamburgerModifier = new Famous.StateModifier
        origin: [0, .5]
        align: [0, .5]
      searchModifier = new Famous.StateModifier
        origin: [.5, .5]
        align: [.5, .5]
      iconModifier = new Famous.StateModifier
        origin: [1, .5]
        align: [1, .5]
      @layout.header
        .add hamburgerModifier
        .add @hamburgerSurface
      @layout.header
        .add searchModifier
        .add searchSurface
      @layout.header
        .add iconModifier
        .add iconSurface
    createBody: ->
      @bodySurface = new Famous.ImageSurface
        size: [undefined, true]
        content: 'img/body.png'
      @layout.content.add @bodySurface
    setListeners: ->
      @hamburgerSurface.on 'click', =>
        @_eventOutput.emit 'menuToggle'
      @bodySurface.pipe @_eventOutput
    createBacking: ->
      backing = new Famous.Surface
        properties:
          backgroundColor: 'black'
          boxShadow: '0 0 20px rgba(0,0,0,0.5)'
      @add backing

client/models/StripView.coffee
$(document).ready ->
  class Timbre.StripView extends Famous.View
    DEFAULT_OPTIONS:
      width: 320
      height: 55
      angle: -0.2
      iconSize: 32
      iconUrl: 'img/strip-icons/famous.png'
      title: 'Famo.us'
      fontSize: 26
    constructor: (@options) ->
      @constructor.DEFAULT_OPTIONS = @DEFAULT_OPTIONS
      super @options
      @createBackground()
      @createIcon()
      @createTitle()
    createBackground: ->
      backgroundSurface = new Famous.Surface
        size: [@options.width, @options.height]
        properties:
          backgroundColor: 'black'
          boxShadow: '0 0 1px rgba(0, 0, 0, 1)'
      rotateModifier = new Famous.StateModifier
        transform: Famous.Transform.rotateZ @options.angle
      skewModifier = new Famous.StateModifier
        transform: Famous.Transform.skew 0, 0, @options.angle
      @add(rotateModifier)
        .add skewModifier
        .add backgroundSurface
    createIcon: ->
      iconSurface = new Famous.ImageSurface
        size: [@options.iconSize, @options.iconSize]
        content: @options.iconUrl
        properties:
          pointerEvents: 'none'
      iconModifier = new Famous.StateModifier
        transform: Famous.Transform.translate 24, 2, 0
      @add(iconModifier).add iconSurface
    createTitle: ->
      titleSurface = new Famous.Surface
        size: [true, true]
        content: @options.title
        properties:
          color: 'white'
          fontSize: "#{@options.fontSize}px"
          textTransform: 'uppercase'
          pointerEvents: 'none'
      titleModifier = new Famous.StateModifier
        transform: Famous.Transform.thenMove(
          Famous.Transform.rotateZ @options.angle
        , [75, -5, 0]
        )
      @add(titleModifier).add titleSurface

8 juin 2014

AdFab makes Badoit sparkling thanks to Famo.us

Famo.us has a physic engine. When used for animating screens, it's simply stunning.

4 juin 2014

Meteor docset is available in Dash

Docsets are offline and searchable docs that you can browse using Dash. Recently, Meteor's docset has been added. Now, even when you are offline, you can easily browse your favorite framework's docs.

You can even call Dash within Vim using the Vimdash plugin.

1 juin 2014

Velocity.js and Meteor

Introduction

Velocity.js is a new animation library in JS. It acts as a replacement of jQuery's animation library. Internally, it uses the CSS3 transformation and animation properties leveraging their hardware acceleration capabilities where possible and falling back on basic jQuery's animation when they are not available. Neat. Here is a small video of a simple animation developed by its creator:
Doing front end development with Meteor is so easy that I've recreated this demo and deployed it in less than one hour. Here is the result: http://velocity3ddemo.meteor.com/

Step-by-step recreation

Create a basic Meteor application:
mrt create Velocity3dDemo
cd Velocity3dDemo
rm -rf Velocity3dDemo*
Import packages:
mrt add coffeescript
mrt add jade
mrt add stylus
mrt add velocityjs
Create a basic web app structure:
mkdir -p client/stylesheets
Create the Stylus file client/stylesheets/app.styl:
@import 'nib'

*
  padding: 0
  margin: 0

body
  background-color: #060b14
  overflow: hidden
  color: #ffffff
  font-family: Helvetica Neue, Open Sans, sans-serif
  font-weight: 100

a
  color: #4bc2f1
  text-decoration: none
  &:hover
    text-decoration: underline

#container
  perspective: 50px
  transform-origin: 50% 100%
  pointer-events: none
  opacity: 0.55

#welcome
  position: fixed
  width: 22rem
  left: 50%
  top: 45%
  margin-top: -1rem
  margin-left: -11rem
  font-weight: 200
  opacity: 0.65
  text-align: center
  font-size: 0.775rem
  line-height: 1.05rem
  letter-spacing: 0.135em
  word-spacing: -0.075rem

@media screen and (max-width: 400px)
  #welcome
    font-size: 0.45rem !important

#logo
  position: fixed
  right: 0.75rem
  bottom: 0.65rem
  cursor: pointer
  text-decoration: none
  color: #d6d6d6
  font-size: 2rem
  letter-spacing: 0.025em

#logoDot
  color: #d74580

.dot
  position: fixed
  width: 30px
  height: 30px
  border-radius: 30px
  background-color: #4bc2f1
Create the Jade file client/index.jade:
head
  title Velocity 3D demo
body
  +index

template(name='index')
  #welcome No WebGL. No Canvas. Just pure DOM.
  a#logo(href='http://julian.com/research/velocity/') Velocity.js
  #container
Create the CoffeeScript file client/index.coffee:
Template.index.rendered = ->
  # Device detection
  isWebkit = /Webkit/i.test navigator.userAgent
  isChrome = /Chrome/i.test navigator.userAgent
  isMobile = window.ontouchstart isnt undefined
  isAndroid = /Android/i.test navigator.userAgent
  isIE = document.documentMode

  # Redirection
  if isMobile and isAndroid and not isChrome
    alert 'Use Chrome on Android'

  # Helpers
  # Randomly generate an integer between 2 numbers.
  r = (min, max) ->
    Math.floor(Math.random() * (1 + max - min)) + min

  # Dot creation
  # Differentiate dot counts on based on device and browser capabilities
  dotsCount = if isMobile then (if isAndroid then 40 else 60) else ( if isChrome then 175 else 125)
  dotsHtml = ''
  for i in [0..dotsCount]
    dotsHtml += '<div class="dot"></div>'
  $dots = $ dotsHtml

  # Setup
  $container = $ '#container'
  $welcome = $ '#welcome'

  screenWidth = window.screen.availWidth
  screenHeight = window.screen.availHeight
  chromeHeight = screenHeight - (document.documentElement.clientHeight or screenHeight)

  translateZMin = - 725
  translateZMax = 600

  containerAnimationMap =
    perspective: [215, 50]
    opacity: [0.90, 0.55]

  # IE10+ produce odd glitching issues when you rotateZ on a parent element subjected to 3D transforms.
  containerAnimationMap.rotateZ = [5, 0] if not(isIE)

  # Animation
  # Fade out the welcome message.
  $welcome.velocity
      opacity: [0, 0.65]
    ,
      display: 'none'
      delay: 3500
      duration: 1100
  # Animate the dot's container.
  $container
    .css 'perspective-origin', "#{screenWidth/2}px #{(screenHeight*0.45)-chromeHeight}px"
    .velocity containerAnimationMap, {duration: 800, loop: 1, delay: 3250}

  # Special visual enhancement for WebKit browsers which are faster at box-shadow manipulation
  ($dots.css 'boxShadow', '0px 0px 4px 0px #4bc2f1') if isWebkit

  $dots
    .velocity
        translateX: [
          -> '+=' + r -screenWidth/2.5, screenWidth/2.5
          -> r 0, screenWidth
        ]
        translateY: [
          -> '+=' + r -screenHeight/2.75, screenHeight/2.75
          -> r 0, screenHeight
        ]
        translateZ: [
          -> '+=' + r translateZMin, translateZMax
          -> r translateZMin, translateZMax
        ]
        opacity: [
          -> Math.random()
          -> Math.random() + 0.1
        ]
      ,
        duration: 6000
        easing: 'easeInOutsine'
    .velocity 'reverse', {easing: 'easeOutQuad'}
    .velocity
        opacity: 0
      ,
        duration: 2000
        complete: ->
          $welcome
            .html "<a href='https://www.youtube.com/watch?v=MDLiVB6g2NY&hd=1'>Watch the making of this demo.</a><br /><br />Go create something amazing.<br />Sincerely, <a href='http://twitter.com/shapiro'>@Shapiro</a>"
            .velocity
                opacity: 0.75
              ,
                duration: 3500
                display: 'block'
    .appendTo $container
Launch it:
mrt
Publish it:
mrt deploy velocity3ddemo.meteor.com
Side note: I've just redeploy this little web app with a link to Meteor. My browser was on the page. Automatically, my browser has been informed that a new release of the code was available. It has only reloaded the modified assets. This is the incredible power of the live reload even when your apps are deployed. Amazing.

The original tutorial

Julian Shapiro, the author of Velocity.js, has provided a very nice Youtube channel which describes how he achieves his demo. A must watch.