Skip to content

Color Flash Shader

Unstable API

The API for defining shaders is a first pass and is likely to change as we get usage feedback. See shader-default.md for more information.

ts
import { Toodle } from "@blooper.gg/toodle";

const canvas = document.querySelector("canvas")!;

const toodle = await Toodle.attach(canvas, {
  filter: "nearest",
  limits: { textureArrayLayers: 5 },
});

await toodle.assets.loadTextures({
  mushroom: new URL("https://toodle.gg/img/Mushroom.png"),
});

const shader = toodle.QuadShader(
  // this is a label for debugging
  "color flash",
  // this is the number of instances that can use this shader.
  // note that if you are defining your own data, defining the shader will
  // allocate the worst case number of instances up front.
  3,
  // this is the wgsl code for the shader.
  // we recommend adding https://marketplace.cursorapi.com/items?itemName=ggsimm.wgsl-literal
  // for syntax highlighting.
  /*wgsl*/ `

// the first struct you specify will be defined as instance data in the vertex instance buffer
// and passed through from the vertex shader to the fragment shader
struct Flash {
  color: vec4f,
  intensity: f32
}

// specifying a fragment entrypoint will override the default fragment shader
@fragment
fn frag(vertex: VertexOutput) -> @location(0) vec4f {
  // default_fragment_shader will return the color of the pixel as sampled from the texture.
  // nearestSampler is a sampler that uses nearest neighbor filtering, you can also use linearSampler
  let color = default_fragment_shader(vertex, nearestSampler);
  // mix is a function that linearly interpolates between two values.
  // here we are interpolating between the color of the pixel and the flash color
  // based on the flash intensity.
  // notice that flash_intensity is defined in the struct above and passed along as vertex instance data.
  let flashColor = mix(color.rgb, vertex.flash_color.rgb, vertex.flash_intensity);
  // return the flash color with the original alpha value.
  return vec4f(flashColor, color.a);
}
  `,
);

const quad = toodle.Quad("mushroom", {
  scale: { x: 4, y: 4 },
  position: { x: 10, y: 10 },
  shader,
  writeInstance: (array, offset) => {
    // this is how you write data to the instance buffer.
    // there is not yet a semantic way to map to the struct you defined above,
    // so you'll have to do some low-level math to get the data in the right place for now.
    const intensity = Math.sin(toodle.diagnostics.frames / 100);
    array.set([1, 0, 1, 1], offset);
    array.set([intensity], offset + 4);
  },
});

async function frame() {
  toodle.startFrame();
  toodle.draw(quad);
  toodle.endFrame();
  requestAnimationFrame(frame);
}

frame();