Save Expand icon

Ron Valstar
front-end developer

Experiment: glass

One of the nicest emergent behaviours I know is recursion. For this experiment a screenshot is taken and placed at a different angle, offset and scale. Then repeated indefinitely. For colouring I added a really light gradient each cycle (otherwise the image would recurse into a single colour).

The result looks mostly fractal.

Click it!

import experiment from './base'
import perlin from '../math/perlin'
import color from '../math/color'

let inst = experiment('glass',{
		init
		,exit
		,handleAnimate
		,handleResize
	})
	,zuper = inst.zuper
  //
  ,noise = perlin.noise
  ,PI = Math.PI
  ,w,h,wHalf,hHalf
  //
  ,target
  ,canvas
  ,context
  //
  ,numbers = [0,0,0,0]
  ,gradient
  ,pattern

function init(_target) {
  target = _target
  canvas = zuper.init(_target)
  canvas.addEventListener('click',handleCanvasClick)
  context = inst.context
  handleResize()
  handleCanvasClick()
  return canvas
}

function exit(){
  zuper.exit()
  canvas.removeEventListener('click', handleCanvasClick)
}

// protected methods

function handleAnimate(deltaT,millis){
  let speed = 0.000002*millis
    ,scale = 1+(noise(numbers[0]-10*speed)-0.5)
    ,rotation = ((2.3+3*noise(numbers[1]+speed))%2)*PI
    ,offset1 = noise(numbers[2]-speed)-0.5
    ,offset2 = noise(numbers[3]+speed)-0.5
    ,offset = scale*2347
    ,offsetX = offset*offset1
    ,offsetY = offset*offset2

  //
  context.beginPath()
  //
  context.globalAlpha = 0.1
  context.fillStyle = gradient
  context.fillRect(0,0,w,h)
  //
  context.globalAlpha = 1
  pattern = context.createPattern(canvas,'repeat')
  context.fillStyle = pattern
  context.save()
  context.rect(0,0,w,h)
  context.translate(wHalf+offsetX,hHalf+offsetY)
  context.scale(scale,scale)
  context.rotate(rotation)
  context.fill()
  context.restore()
  //
  context.closePath()
  //
  pattern = null
}

function handleResize(){
  zuper.handleResize()
  w = inst.w
  h = inst.h
  wHalf = w/2
  hHalf = h/2
}

// private methods

function handleCanvasClick() {
  let gradientColor = color(0).randomize().lightness(0.1).toString()
  gradient = context.createLinearGradient(0,0,0,h)
  gradient.addColorStop(0,gradientColor)
  gradient.addColorStop(1,gradientColor)
  for (let i = 0,l=1+(5*Math.random()<<0); i < l; i++) {
    let fStop = (1+i)*(1/(l+1))
    gradient.addColorStop(fStop,color(0).randomize().saturation(1).toString())
  }
  context.fillStyle = gradient
  context.fillRect(0,0,w,h)
  //
  numbers.forEach(function(o,i) {
    numbers[i] = Math.random()*1E9<<0
  })
}

export const glass = inst.expose

comment send comment