Live Background

As I was running through the other Perlin Noise examples I realized I could - now - very easily recreate this blog’s background dynamically.

What I’m doing is very similar to the 2D mapping, except I now space out pixels a bit further and I navigate the Perlin Noise space on the Z axis, effectively going up on that space while everything else is mapped directly to x, y positions. The rest of the code is there to try and handle light / dark user preferences, although right now it works best in dark.

The aesthetics are a bit too subtle and that’s always been my weakest side, but it was nice to make this connection and I felt it was worth the work.

All the code that runs this experiment is bellow, apart from any external libraries (i.e. p5.js)

'use strict'

// Avoiding Global Mode: https://github.com/processing/p5.js/wiki/Global-and-instance-mode
const perlin_noise = ( sketch ) => {

  // This is the element where we'll create our P5 canvas
  let container = document.getElementById('sketch-holder')

  let start = 0         // Where in the Perlin Noise graph we'll begin looking for values
  let increment = 0.05  // How close together the values will be
  let colorSchemeQuery = window.matchMedia('(prefers-color-scheme: dark)')
  let backgroundColor = colorSchemeQuery.matches ? 0: 255
  let grid_spacing = 12
  let darkPointColorRange = [50, 255]
  let lightPointColorRange = [0, 150]

  sketch.setup = () => {
    var canvas = sketch.createCanvas(container.clientWidth, container.clientHeight)
    canvas.parent(container)

    setBackground()
    sketch.noiseDetail(8, 0.6)
    sketch.frameRate(10)
    sketch.strokeWeight(2)
    sketch.pixelDensity(1)

    colorSchemeQuery.addEventListener('change', () => {
      backgroundColor = colorSchemeQuery.matches ? 0 : 255
      setBackground()
    })
  }

  function setBackground() {
    sketch.background(backgroundColor, backgroundColor, backgroundColor)
  }

  sketch.windowResized = () => {
    sketch.resizeCanvas(container.clientWidth, container.clientHeight)
    setBackground()
    sketch.draw()
  }

  sketch.draw = () => {
    var z_offset = start
    var density = sketch.pixelDensity()
    var colorRange = colorSchemeQuery.matches ? darkPointColorRange : lightPointColorRange
    var inverseColorRange = colorSchemeQuery.matches ? lightPointColorRange : darkPointColorRange
    // This maps noise values onto a 2D space
    for(var x = 0; x < sketch.width * density; x += grid_spacing * density) {
      for(var y = 0;  y < sketch.height * density; y += grid_spacing * density) {
        var index = (x + y * sketch.width * density) * 4;
        var color = sketch.map(sketch.noise(x, y, z_offset), 0, 1, ...colorRange)

        sketch.stroke(color)
        sketch.point(x, y)
      }
    }

    start += increment
  }
}

// Wait for everything to load
if (document.readyState === 'complete') {
  new p5(perlin_noise)
} else {
  window.onload = (event) => {
    new p5(perlin_noise)
  }
}