Effect feel

Every effect has a set of dials from 0 to 1, named for what you see: how far a glow reaches, how torn an edge is, how worn the press. Turn a dial and the effect’s numbers follow. Leave it alone and the effect looks exactly as it always has.

import { composeEffects, fill } from "shodo";
import { effectFeel, getEffect } from "shodo/registry";

const glow = getEffect("glow")!;
const shader = composeEffects(fill(), glow.create(effectFeel(glow, { reach: 0.8, brightness: 0.3 })));

That is a wide, faint glow. effectFeel returns plain uniform values, so they go wherever values go: create(), a setter, a preset.

Fill, outline, glow and grain on one word. Wipe, scramble and wake need motion, so they live in the playground.open in the playground

The dials

Each dial rests where the effect’s defaults put it: a glow’s reach rests at 0.5, its brightness at a third.

fill1 dial
  • presence
    ghostsolid

    How solid the letters are.

wipe1 dial
  • softness
    hard cutslow fade

    How soft the edge of the reveal is.

outline1 dial
  • weight
    hairlineheavy

    How heavy the line around each letter is.

glow2 dials
  • reach
    tightwide

    How far the light spreads from the letters.

1 more dial
  • brightness
    faintblazing

    How strong the light is.

scramble1 dial
  • pace
    slowfrantic

    How fast the letters flicker through other characters.

grain3 dials
  • grit
    cleangritty

    How much noise sits over the letters.

2 more dials
  • coarseness
    finecoarse

    How big each speck of grain is.

  • life
    stillboiling

    How fast the grain moves.

wake3 dials
  • pull
    lightheavy

    How hard the letters are dragged behind the pointer.

2 more dials
  • reach
    closefar

    How far from the pointer letters still feel it.

  • linger
    snaps backlingers

    How long the letters take to settle back.

rough3 dials
  • roughness
    clean cuttorn

    How torn the letters’ edges are.

2 more dials
  • scale
    finecoarse

    How big the bumps along the edge are.

  • detail
    smoothbusy

    How much small wobble rides on the big bumps.

dotgain2 dials
  • swell
    starvedswollen

    How much the letters fatten as ink spreads into the paper.

1 more dial
  • bleed
    crispbleeding

    How soft the spread ink’s edge is.

inkpool3 dials
  • pooling
    drypooled

    How dark the ink gathers at the letters’ edges.

2 more dials
  • spread
    tightwide

    How far in from the edge the pooled ink reaches.

  • starve
    evenstarved

    How much the middle of each letter runs out of ink.

press2 dials
  • wear
    freshworn

    How uneven the ink lies, like a worn printing plate.

1 more dial
  • voids
    solidpatchy

    How many bare patches the press leaves.

liquid1 dial
  • swell
    thinnedswollen

    How much the letters thin or swell, like ink on wet paper.

dither2 dials
  • strength
    offfull

    How much of the dither shows.

1 more dial
  • chunk
    finechunky

    How big each dither pixel is.

halftone3 dials
  • screen
    fine screencoarse screen

    How big the halftone dots are.

2 more dials
  • ink
    lightheavy

    How much ink each dot carries.

  • softness
    crispsoft

    How soft each dot’s edge is.

Each definition carries its own in definition.feel: the title, the sentence, the two end words, the uniforms it moves and where it rests. A panel can be built from that alone.

Giving your own effect dials

Add feel to the spec. A dial names the uniforms it moves and their values at 0 and at 1:

defineEffect({
  id: "pulse",
  // ...
  uniforms: { beat: { type: "float", value: 1, min: 0, max: 8, unit: "per second", description: "Beats per second." } },
  feel: {
    tempo: { title: "Tempo", summary: "How fast it beats.", ends: ["slow", "fast"], drives: { beat: [0, 4] } },
  },
});

You don’t say where the dial rests: defineEffect works it out from the default (beat 1 between 0 and 4 rests at 0.25). It refuses a dial that names a uniform the effect lacks, that moves anything but a single float, that reaches outside a uniform’s range, whose default lies outside its ends, or whose uniforms would rest at different points. A dial moves in a straight line between its ends; anything subtler belongs in the uniforms themselves.

Dials are optional and add nothing to the shader: an effect without them works as before, under contract version 1. See writing-effects.md for the rest of a spec.