Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How to lie cheat and steal

How to lie cheat and steal

Better browsers, more CPU cores, faster Javascript engines — performance on the client side has been improving rapidly over the past years. And with HTML5 web app developers now have more possibilities than ever to take advantage of all this power.

Time to move some of the heavy lifting from the server to the client. Time to “lie, cheat, and steal”, as Aaron Patterson put it in his RubyConf keynote.

Experimentation is the foundation for this talk, so put on your lab coats. You might not want to put every bit of code you’ll see into your production apps, but you may just get some new (and wild) ideas. Make the browser work for your (Rails) app!

Florian Plank

March 02, 2013
Tweet

More Decks by Florian Plank

Other Decks in Programming

Transcript

  1. How to lie,
    cheat and steal

    View Slide

  2. @polarblau

    View Slide

  3. View Slide

  4. How to lie,
    cheat and steal

    View Slide

  5. View Slide

  6. Ruby 2.0
    Rails 4.0

    View Slide

  7. View Slide

  8. View Slide

  9. http://blog.airbrake.io/guest-post/exploring-everything/

    View Slide

  10. How does it work?

    View Slide

  11. Not well …

    View Slide

  12. View Slide

  13. ctx = $($canvas).get(0).getContext('2d')
    preview = $($preview).get(0)
    localMediaStream = null
    start = null

    View Slide

  14. start: ->
    start = (new Date).getTime()
    started = true
    isStarted: ->
    started? and started
    stop: ->
    started = false
    isStopped: ->
    started == false

    View Slide

  15. success = (stream) =>
    preview.src = window.URL.createObjectURL(stream)
    localMediaStream = stream
    preview.addEventListener 'loadeddata', capture
    error = ->
    console.error arguments
    navigator.getUserMedia video: true, success, error

    View Slide

  16. View Slide

  17. View Slide

  18. capture: =>
    ctx.drawImage(preview, 0, 0) if localMediaStream?
    now = (new Date).getTime()
    pixels = ctx.getImageData(0, 0, WIDTH, HEIGHT).data
    timestamp = now - start
    createFrame(pixels, timestamp)
    requestAnimationFrame capture unless isStopped()

    View Slide

  19. v
    [red, green, blue, alpha]

    View Slide

  20. createFrame = (pixels, timestamp) ->
    intensity = 0
    intensity += pixel for pixel in pixels by 4 when pixel?
    { intensity: intensity, timestamp: timestamp }

    View Slide

  21. v
    [red, green, blue, alpha]

    View Slide

  22. View Slide

  23. View Slide

  24. View Slide

  25. 86

    View Slide

  26. window.URL ?= window.webkitURL
    window.requestAnimationFrame ?=
    window.webkitRequestAnimationFrame
    navigator.getUserMedia ?= navigator.webkitGetUserMedia

    View Slide

  27. View Slide

  28. Server
    Client
    Request
    Response

    View Slide

  29. Server
    Request
    Response
    Browser

    View Slide

  30. Server
    Request
    Response
    Browser

    View Slide

  31. View Slide

  32. Can it run Crysis?

    View Slide

  33. crack = (hash, maxLength = 10, charset) ->
    charset ?= (
    'abcdefghijklmnopqrstuvwxyz' +
    'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
    '0123456789'
    ).split('')
    stack = ['']
    for [1..maxLength]
    buffer = []
    for item in stack
    for character in charset
    word = item + character
    if md5(word) == hash
    return word
    else
    buffer.push word
    stack = stack.concat buffer

    View Slide

  34. hash = '3858f62230ac3c915f300c664312c63f' # "foobar"
    crack(hash)

    View Slide

  35. def crack(hash, max_length = 10, charset = nil)
    charset ||= (
    'abcdefghijklmnopqrstuvwxyz' +
    'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
    '0123456789'
    ).split('')
    stack = ['']
    max_length.times do
    buffer = []
    for item in stack
    for character in charset
    word = item + character
    if Digest::MD5.hexdigest(word) == hash
    return word
    else
    buffer << word
    end
    end
    end
    stack = stack + buffer
    end
    end

    View Slide

  36. Javascript Ruby

    View Slide

  37. v

    View Slide

  38. Square one

    View Slide

  39. View Slide

  40. Try to solve problems
    closer to the GUI

    View Slide

  41. View Slide

  42. View Slide

  43. View Slide



  44. View Slide

  45. url = window.URL || window.webkitURL

    View Slide

  46. # Creates a new object URL.
    window.URL.createObjectURL()
    # Revokes an object URL previously created
    window.URL.revokeObjectURL()

    View Slide

  47. video = document.createElement 'video')

    View Slide

  48. $('#source').on 'change', ->
    src = url.createObjectURL(@files[0])
    video.src = src
    video.autoplay = false
    video.load()

    View Slide

  49. THUMBNAILSIZE = 0.25
    $(video).on 'canplaythrough', ->
    width = video.videoWidth * THUMBNAILSIZE
    height = video.videoHeight * THUMBNAILSIZE
    extractFrame(video, width, height)

    View Slide

  50. step = 1 # extract a frame every on second
    extractFrame = (video, width, height, time = 0) ->
    video.currentTime = time
    $(video).one 'seeked', ->
    ctx = createAndInsertCanvas(width, height)
    ctx.drawImage(video, 0, 0, width, height)
    time += step
    if time < video.duration
    extractFrame(video, width, height, time)
    else
    $('#output')
    .show()
    .on('mousemove', scrub)
    .find('canvas:not(:last)')
    .hide()

    View Slide

  51. $output = $('#output')
    outputOffset = $output.offset().left
    outputWidth = $output.width()
    pages = $output.find('canvas')
    pageCount = pages.length
    stepSize = outputWidth / (pageCount - 1)
    scrub = (e) ->
    page = Math.round((e.pageX - outputOffset) / stepSize)
    pages.hide().eq(page).show()

    View Slide

  52. View Slide

  53. Potential issues —

    View Slide

  54. Potential issues —
    Video format

    View Slide

  55. Potential issues —
    Video format
    Canvas support

    View Slide

  56. Potential issues —
    Video format
    Canvas support
    Javascript support

    View Slide

  57. View Slide

  58. View Slide

  59. View Slide

  60. alt="Strange things in space"
    src="space.jpg"
    data-width="200"
    data-height="100" />

    View Slide

  61. width = $image.data('width')
    height = $image.data('height')
    src = $image.attr('src')
    $canvas = $('')
    $canvas.attr(width: width, height: height)
    ctx = $canvas.get(0).getContext('2d')
    image = new Image()
    image.onload = ->
    ratio = if width > height then height / width
    else height / height
    [sWidth, sHeight] = if width > height
    [@width, @width * ratio]
    else
    [@height * ratio, @height]
    ctx.drawImage(@, 0, 0, sWidth, sHeight,
    0, 0, width, height)
    $image.replaceWith $canvas

    View Slide

  62. ctx.drawImage(
    image, # image object
    sx, # crop positition
    sy, #
    sWidth, # crop size
    sHeight, #
    dx, # target position
    dy, #
    dWidth, # target size
    dHeight #
    )

    View Slide

  63. alt="Strange things in space"
    src="space.jpg"
    data-width="200"
    data-height="100"
    data-zoom="false"
    data-gravity="sw" />

    View Slide

  64. $('img.resize').resize()
    $('img.resize').resize(gravity: 'ne', zoom: false)
    # resize, crop if necessary to maintain aspect ratio
    # north-east gravity
    $('img.resize').resize('400x300#ne')
    # resize only if the image is smaller than this
    $('img.resize').resize('400x300<')

    View Slide

  65. View Slide

  66. Potential issues —

    View Slide

  67. Potential issues —
    Canvas support

    View Slide

  68. Potential issues —
    Canvas support
    Javascript support

    View Slide

  69. But, can it run Crysis?

    View Slide

  70. ORANGE

    View Slide

  71. v
    [red, green, blue, alpha]

    View Slide

  72. createFrame = (pixels, timestamp) ->
    intensity = 0
    intensity += pixel for pixel in pixels by 4 when pixel?
    { intensity: intensity, timestamp: timestamp }
    &
    requestAnimationFrame capture unless isStopped()

    View Slide

  73. Web Workers

    View Slide

  74. ORANGE

    View Slide

  75. Orange = require 'orange'

    View Slide

  76. set = new Orange.JobSet
    set.on 'complete', ->
    console.log 'All done'

    View Slide

  77. job = new Orange.Job 'calculate_intensity',
    pixels : pixels
    timestamp: timestamp
    job.on 'complete', (data) =>
    console.log data.intensity
    job.perform()
    # or run whole set at once
    set.push(job)
    set.perform()

    View Slide

  78. self.onmessage = (event) ->
    [type, data] = [event.data.type, event.data.data]
    switch type
    when 'perform'
    intensity = 0
    intensity += pixel for pixel in data.pixels by 4 ↵
    when pixel?
    self.postMessage
    type: 'complete'
    data:
    intensity: intensity
    timestamp: data.timestamp

    View Slide

  79. View Slide

  80. The Future —

    View Slide

  81. The Future —

    View Slide

  82. The Future —
    Worker API

    View Slide

  83. The Future —
    Worker API
    Persistent jobs (?)

    View Slide

  84. The Future —
    Worker API
    Persistent jobs (?)
    Worker class shim

    View Slide

  85. The Future —
    Worker API
    Persistent jobs (?)
    Worker class shim
    Queue interface

    View Slide

  86. The Future —
    Worker API
    Persistent jobs (?)
    Worker class shim
    Queue interface
    Custom events

    View Slide

  87. The Future —
    Worker API
    Persistent jobs (?)
    Worker class shim
    Queue interface
    Custom events
    Job retry

    View Slide

  88. The Future —
    Worker API
    Persistent jobs (?)
    Worker class shim
    Queue interface
    Custom events
    Job retry
    Error handling

    View Slide

  89. Why not?

    View Slide

  90. Fundamental lack of trust

    View Slide

  91. window.onbeforeunload = ->
    'Are you sure you want to leave?'

    View Slide

  92. Local storage

    View Slide

  93. View Slide

  94. Shims / Shivs
    https://github.com/Modernizr/Modernizr/wiki/
    HTML5-Cross-Browser-Polyfills

    View Slide

  95. Dziękuję!
    Thanks!

    View Slide