Smoke Text

← All Butter documentation

Notes:

  • Make sure Draws Using Previous Frame State is turned on
let prev, next
let txt
let font
let size
let smoke

async function setup() {
  font = await loadFont('')
  size = createSlider(25, 100, 80, 0.1)
  txt = createInput('')
  createCanvas(
    windowWidth,
    windowHeight,
    WEBGL
  )

  next = createFramebuffer()
  prev = createFramebuffer()

  smoke = baseFilterShader().modify(() => {
    const t = uniformFloat(() => millis() * 0.001)

    const warped = (coord) =>
      coord + 0.002 *
        (noise(coord.x*5, coord.y*5, t) - 0.3) +
        [0, 0.002]
    
    getColor((inputs, canvasContent) => {
      let coord = warped(inputs.texCoord)
      let neighbor = warped(
        inputs.texCoord + [inputs.texelSize.x, 0]
      )
      let differenceIn = inputs.texelSize.x
      let differenceOut = length(coord - neighbor)
      let compression = differenceIn/differenceOut
      let c = getTexture(canvasContent, coord)
      // Slightly fade out
      c = mix(c, [0,0,0,0], 0.05)
      // Change opacity based on smoke compression
      c.a /= compression
      return c
    })
  })
}

function draw() {
  // Swap prev and next buffers
  [prev, next] = [next, prev];

  imageMode(CENTER)

  next.begin()
  clear()

  image(prev, 0, 0)
  filter(smoke)

  textAlign(CENTER, CENTER)
  textFont(font)
  textSize(30)
  let txtW = fontWidth(txt.value())
  let txtH = textLeading() *
    txt.value().split('\n').length
  scale(
    min(width/txtW, height/txtH) *
    size.value()/100
  )
  text(txt.value(), 0, 0)
  next.end()

  clear()
  image(next, 0, 0)
}