Skip to content
Shader

Aurora Gradient

An animated fill shader where teal, violet and pink slowly drift

  • #shader
  • #gradient
  • #aurora
  • #fill
  • #animated

Install

This item can't be installed yet. Use the source as a reference. Why some items can't be installed

AI prompt

Create a WebGPU fill shader that blends three sine waves into a teal/violet/pink gradient, driven by frame.time and two sliders: speed and intensity.
demo.tsxtsx
"use client";

import { ShaderCanvas } from "@devs7udio/demo-kit/shader";
import * as shader from "./main.web";

export default function Demo() {
  return <ShaderCanvas module={shader} className="h-screen w-full" />;
}
main.tsts
import { defineProperties } from "figma:shaders"

export default function Fill() {}

export function setup(device, frame) {
  frame.state.module = device.createShaderModule({
    code: `
diagnostic(off,derivative_uniformity);

struct Uniforms { time: f32, speed: f32, intensity: f32, aspect: f32 }
@group(0) @binding(0) var<uniform> u: Uniforms;

struct VSOut { @builtin(position) pos: vec4f, @location(0) uv: vec2f }

@vertex
fn vs_main(@location(0) position: vec2f, @location(1) uv: vec2f) -> VSOut {
  var o: VSOut;
  o.pos = vec4f(position, 0.0, 1.0);
  o.uv = uv;
  return o;
}

@fragment
fn fs_main(frag: VSOut) -> @location(0) vec4f {
  let t = u.time * u.speed;
  var p = frag.uv;
  p.x = p.x * u.aspect;
  let w1 = sin(p.x * 3.0 + t * 0.9) * 0.5 + 0.5;
  let w2 = sin(p.y * 4.0 - t * 0.7 + p.x * 1.5) * 0.5 + 0.5;
  let w3 = sin((p.x + p.y) * 2.5 + t * 1.3) * 0.5 + 0.5;
  let a = vec3f(0.10, 0.85, 0.65);
  let b = vec3f(0.45, 0.35, 0.95);
  let c = vec3f(0.95, 0.40, 0.75);
  var col = mix(a, b, w1);
  col = mix(col, c, w2 * 0.6);
  let glow = (w3 * 0.5 + 0.25) * u.intensity;
  col = col * (0.35 + glow);
  return vec4f(col, 1.0);
}`,
  })

  frame.state.quad = device.createBuffer({
    size: 6 * 4 * 4,
    usage: GPUBufferUsage.VERTEX,
    mappedAtCreation: true,
  })
  new Float32Array(frame.state.quad.getMappedRange()).set([
    -1, -1, 0, 1,  1, -1, 1, 1,  -1, 1, 0, 0,
    -1, 1, 0, 0,   1, -1, 1, 1,   1, 1, 1, 0,
  ])
  frame.state.quad.unmap()

  frame.state.uniforms = device.createBuffer({
    size: 16,
    usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
  })
}

export function render(device, frame) {
  const state = frame.state
  const output = frame.output

  if (state.pipelineFormat !== output.format) {
    state.pipeline = device.createRenderPipeline({
      layout: "auto",
      vertex: {
        module: state.module,
        entryPoint: "vs_main",
        buffers: [{
          arrayStride: 16,
          attributes: [
            { shaderLocation: 0, format: "float32x2", offset: 0 },
            { shaderLocation: 1, format: "float32x2", offset: 8 },
          ],
        }],
      },
      fragment: {
        module: state.module,
        entryPoint: "fs_main",
        targets: [{ format: output.format }],
      },
      primitive: { topology: "triangle-list" },
    })
    state.bindGroup = device.createBindGroup({
      layout: state.pipeline.getBindGroupLayout(0),
      entries: [{ binding: 0, resource: { buffer: state.uniforms } }],
    })
    state.pipelineFormat = output.format
  }

  device.queue.writeBuffer(
    state.uniforms,
    0,
    new Float32Array([
      Number(frame.time) * 0.001,
      frame.params.speed,
      frame.params.intensity,
      output.width / output.height,
    ]),
  )

  const encoder = device.createCommandEncoder()
  const pass = encoder.beginRenderPass({
    colorAttachments: [{
      view: output.createView(),
      loadOp: "clear",
      storeOp: "store",
      clearValue: { r: 0, g: 0, b: 0, a: 0 },
    }],
  })
  pass.setPipeline(state.pipeline)
  pass.setBindGroup(0, state.bindGroup)
  pass.setVertexBuffer(0, state.quad)
  pass.draw(6)
  pass.end()
  device.queue.submit([encoder.finish()])
}

defineProperties(Fill, {
  speed: { type: "number", control: "slider", min: 0, max: 3, step: 0.01, defaultValue: 1 },
  intensity: { type: "number", control: "slider", min: 0, max: 2, step: 0.01, defaultValue: 1 },
})