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 = buildFilterShader(() => {
    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]
    
    filterColor.begin()
    let coord = warped(filterColor.texCoord)
    let neighbor = warped(
      filterColor.texCoord + [filterColor.texelSize.x, 0]
    )
    let differenceIn = filterColor.texelSize.x
    let differenceOut = length(coord - neighbor)
    let compression = differenceIn/differenceOut
    let c = getTexture(filterColor.canvasContent, coord)
    // Slightly fade out
    c = mix(c, [0,0,0,0], 0.05)
    // Change opacity based on smoke compression
    c.a /= compression
    filterColor.set(c)
    filterColor.end()
  })
}

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)
}