cssAudio - Activefile-genericCSS - ActiveGeneric - ActiveHTML - ActiveImage - ActiveJS - ActiveSVG - ActiveText - Activefile-genericVideo - Activehtmlicon-personicon-teamoctocatspinnerstartv

Pen Settings

CSS Base

Vendor Prefixing

Add External CSS

These stylesheets will be added in this order and before the code you write in the CSS editor. You can also add another Pen here, and it will pull the CSS from it. Try typing "font" or "ribbon" below.

Quick-add: + add another resource

Add External JavaScript

These scripts will run in this order and before the code in the JavaScript editor. You can also link to another Pen here, and it will run the JavaScript from it. Also try typing the name of any popular library.

Quick-add: + add another resource

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

            
              html, body, canvas {
  height: 100%;
  margin: 0;
  overflow: hidden;
  padding: 0;
  width: 100%;
}
            
          
!
            
              // Set up the canvas
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
document.body.appendChild(canvas)

const loop = MainLoop
const pn = new Perlin(Math.random().toString(36).substring(7))

let zoff, zinc,
    inc,
    scale, qty,
    easing,
    cols, rows,
    flowfield

const resizeField = function () {
  zoff = 0
  zinc = 0.00005
  inc = 0.005
  qty = 350
  easing = 100
  
  canvas.width = document.body.clientWidth
  canvas.height = document.body.clientHeight
  ctx.canvas.width = canvas.width
  ctx.canvas.height = canvas.height  
  
  scale = Math.floor(ctx.canvas.width * 0.03)
  cols = Math.floor((canvas.width / scale) + scale)
  rows = Math.floor((canvas.height / scale) + scale)
  flowfield = new Array(cols * rows)
}

resizeField()


// How 'bout a snowflake model
const Snowflake = function () {
  let self = this
  let biggest = 7
  let smallest = 1
  let slowest = 1
  let fastest = 2
  let minBlur = 2 
  let maxBlur = 8
  
  this.init = function () {
    self.pos = {
      x: randomInt(0, ctx.canvas.width - scale),
      y: randomInt(0, ctx.canvas.height  - scale),
      z: map(randomInt(0, ctx.canvas.width), 0, ctx.canvas.width, 0, 1)
    }
    
    self.opacity = self.pos.z
    self.size = map(self.pos.z, 0, 1, smallest, biggest)
    self.blur = map(self.size, smallest, biggest, minBlur, maxBlur)
    
    self.jitter = {
      threshold: randomInt(10, 50) * self.pos.z,
      current: 0,
      vel: self.size / (easing / randomInt(10, 15))
    }
    
    // TODO: use perlin noise for jitter maybe?
    self.pn = new Perlin(Math.random().toString(36).substring(7))
    self.noise = self.pn.noise(zoff, 0, 0)
    
    self.jitter.current = randomInt(-self.jitter.threshold + 1, self.jitter.threshold - 1)
    if (self.jitter.current < 0) {
      self.jitter.vel = -(self.jitter.vel)
    }
    
    self.vel = {
      x: 0,
      y: 0 
    }
    
    self.acc = {
      x: 0,
      y: 0
    }
    
    self.limitx = map(self.pos.z, 0, 1, slowest, fastest) / 1.75
    self.limity = map(self.pos.z, 0, 1, slowest, fastest) / 2
  }
  
  this.follow = function (vectors) {
    let x = Math.floor(self.pos.x / scale)
    let y = Math.floor(self.pos.y / scale)
    let i = x + y * cols
    let force = vectors[i]
    
    self.applyForce(force)
  }
  
  this.applyForce = function (force) {
      self.jitter.current += self.jitter.vel
      
      if ((self.jitter.current >= self.jitter.threshold) || (self.jitter.current <= -self.jitter.threshold)) {
        self.jitter.vel *= -1
      }
      self.acc.x += (force.x + self.jitter.vel) / easing
      self.acc.y += force.y / easing
  }
  
  this.edges = function () {
    if (self.pos.x < 0) {
      self.pos.x = canvas.width
    }
    
    if (self.pos.x > canvas.width) {
      self.pos.x = 0
    }
    
    if (self.pos.y < 0) {
      self.pos.y = canvas.height
    }
    
    if (self.pos.y > canvas.height) {
      self.pos.y = 0
    }
  }
  
  this.update = function () {
    self.follow(flowfield) 
    
    let velx = self.vel.x + self.acc.x
    let vely = self.vel.y + self.acc.y
    self.noise = self.pn.noise(zoff, 0, 0) * randomInt(-1, 1);
    
    if (velx > self.limitx) {
      velx = self.limitx
    }
    
    if (velx < -self.limitx) {
      velx = -self.limitx
    }
    
    if (vely > self.limity) {
      vely = self.limity
    }
    
    if (vely < -self.limity) {
      vely = -self.limity
    }
    
    self.vel.x = velx
    self.vel.y = vely
    
    self.pos.x += self.vel.x
    self.pos.y += self.vel.y
    
    self.acc.x = 0 
    self.acc.y = 0
    
    self.edges()
  }
  
  this.draw = function () {
    let r = self.size
    let gradient = ctx.createRadialGradient(self.pos.x, self.pos.y, r, self.pos.x, self.pos.y, 0)
    let startx = self.pos.x - r,
        starty = self.pos.y - r
    
    gradient.addColorStop(0.65, 'rgba(255, 255, 255, ' + self.opacity + ')')
    gradient.addColorStop(0, 'rgba(255, 255, 255, 0)')
    
    ctx.fillStyle = gradient
    ctx.fillRect(startx, starty, self.size * 2, self.size * 2)
  }
  
  this.init()
}

let flakes = []

const initFlakes = function () {
  for (let i = 0, max = qty; i < max; i++) {
    flakes.push(new Snowflake)
  }  
}

initFlakes()

const update = function () {
  let yoff = 0
  
  for (let y = 0; y < rows; y++) {
    let xoff = 0
    
    for (let x = 0; x < cols; x++) {
      let i = x + y * cols
      let angle = pn.noise(xoff, yoff, zoff) * Math.PI
      xoff += inc
      
      let v = {
        x: Math.cos(angle),
        y: Math.sin(angle)
      }
      
      flowfield[i] = v
    }
    
    yoff += inc
    zoff += zinc
  }
  
  for (let flake of flakes) {
    flake.update()
  }
}

const draw = function () {
  let fill = ctx.createLinearGradient(0, 0, 0, canvas.height)
  fill.addColorStop(0, 'seashell')
  fill.addColorStop(1, 'coral')
  ctx.fillStyle = fill
  ctx.fillRect(0, 0, canvas.width, canvas.height)
  
  for (let flake of flakes) {
    flake.draw()
  }
}

const end = function (fps, panic) {
  if (panic) {
    var discardedTime = Math.round(MainLoop.resetFrameDelta());
  }
}

loop
  .setUpdate(update)
  .setDraw(draw)
  .setEnd(end)
  .start()

window.addEventListener('resize', resizeHandler)


function resizeHandler (e) {
  loop.stop() 
  
  window.setTimeout(function () {
    flakes = []
    flowfield = []
    resizeField()
    initFlakes()
    loop.start()
  }, 250)
}

// http://stackoverflow.com/a/1527820/343520
 function randomInt (min, max) {
   return Math.floor(Math.random() * (max - min + 1)) + min;
 }

  // https://github.com/processing/p5.js/blob/master/src/math/calculation.js#L364-L401
function map (n, start1, stop1, start2, stop2) {
  return ((n-start1)/(stop1-start1))*(stop2-start2)+start2;
}
  

            
          
!
999px
Close

Asset uploading is a PRO feature.

As a PRO member, you can drag-and-drop upload files here to use as resources. Images, Libraries, JSON data... anything you want. You can even edit them anytime, like any other code on CodePen.

Go PRO

Loading ..................

Console