Sensitivity of the sketch to mouse coordinates.
sensitivity = 0.02Base of the iterations of the attractor per frame.
iterations = 8000Each time the attractor hits a pixel, the density increases by…
density = 2Start x,y coordinate for the attractor.
start = 0The number of frames to render before being finished.
limit = 200Size of the canvas in dimensional and then real pixels.
size = Math.round(document.body.clientWidth - (document.body.clientWidth * 0.3))
N = size * (window.devicePixelRatio or 1)Create the canvas element and grab its drawing context.
canvas = document.createElement 'canvas'
canvas.width = canvas.height = N
canvas.style.width = canvas.style.height = "#{size}px"
canvas.style.marginTop = canvas.style.marginLeft = "-#{ size / 2 }px"
document.body.appendChild canvas
context = canvas.getContext '2d'Pull some handy math functions off the Math object, for convenient access.
{round, log, max, sin, cos} = MathInitialize the mouse coordinates to our start.
mouseX = mouseY = startOur Sketch class handles drawing to the <canvas> element.
class Sketch
  constructor: ->
    @steps     = 0
    @stopped   = no
    @button    = document.getElementById 'permalink'
    @button.addEventListener  'mousedown', @permalink,  no
    document.addEventListener 'mousedown', @pause,      no
    document.addEventListener 'mousemove', @record,     no
    document.addEventListener 'mouseup',   @resume,     no
    @initialSeed()
    @attractor = new DeJongAttractor
    @loop()
  loop: ->
    @interval = setInterval @tick, 0
  stopLoop: ->
    @interval = clearInterval @intervalDraw a single frame of the sketch.
  tick: =>
    return @attractor.reseed() if @stopped
    @steps += 1
    @attractor.plot 5
    @stopLoop() if @steps > limitDetermine the initial seed.
  initialSeed: ->
    if hash = window.location.hash.replace('#', '')
      [mouseX, mouseY] = (parseInt(num, 10) for num in hash.split(','))Pause the drawing, when we’ve rendered enough steps.
  pause: (e) =>
    e.preventDefault()
    @stopped = yes
    @loop() unless @intervalRecord the current mouse position.
  record: (e) =>
    if @stopped
      mouseX = e.pageX - canvas.offsetLeft
      mouseY = e.pageY - canvas.offsetTopResume drawing the sketch.
  resume: =>
    @stopped = no
    @steps   = 0Save the permalink of the currently-drawn attractor to the URL.
  permalink: (e) =>
    e.stopPropagation()
    window.location.hash = mouseX + ',' + mouseYThe DeJongAttractor contains the Peter De Jong algorithm.
class DeJongAttractor
  constructor: ->
    @reseed()Clear the recorded exposures before seeding at a different location.
  clear: ->
    @image      = context.createImageData N, N
    @density    = (0 for i in [0...N] for j in [0...N])
    @maxDensity = 0Seed the sketch at the current position of the mouse.
  seed: ->
    @xSeed   = (mouseX * 2 / N - 1) * sensitivity
    @ySeed   = (mouseY * 2 / N - 1) * sensitivity
    [@x, @y] = [N / 2, N / 2]De Jong’s attractor. Iterates a large number of times through random
coordinates in the attractor space, exposing the @density array.
  populate: (samples) ->
    for i in [0...samples * iterations]
      x = ((sin(@xSeed * @y) - cos(@ySeed * @x)) * N * 0.2) + N / 2
      y = ((sin(-@xSeed * @x) - cos(-@ySeed * @y)) * N * 0.2) + N / 2
      @density[round x][round y] += density
      [@x, @y] = [x, y]
    @maxDensity = log max.apply(Math, (max.apply(Math, row) for row in @density))
  reseed: ->
    @clear()
    @seed()
    @plot 3Soft light color blend between two brighness values.
  softLight: (a, b) ->
    ((a * b) >> 7) + ((a * a) >> 8) - ((a * a * b) >> 15)Plots each pixel on the canvas as ImageData, using the @maxDensity to
adjust for over- or under-exposure.
  plot: (samples) ->
    @populate samples
    data = @image.data
    for i in [0...N]
      for j in [0...N]
        dens = @density[i][j]
        idx = (i * N + j) * 4
        data[idx + 3] = 255
        continue if dens <= 0
        light = log(dens) / @maxDensity * 255
        current = data[idx]
        color = @softLight light, current
        data[idx] = data[idx + 1] = data[idx + 2] = color
    context.putImageData @image, 0, 0Kick it off by creating the sketch.
new Sketch()