Skip to content

Effects

With WebGL, we can use GLSL shaders to create advanced effects that are computed directly on the GPU, ensuring high performance. An effect can range from making the clip move in waves to applying a blur. You can apply these effects to any clips added to the composition.

Example: Glow Effect
glsl
precision highp float;
varying vec2 vTextureCoord;

uniform sampler2D uSampler;
const float uAngle = 5.0;
const float uScale = 1.0;
const bool uGrayScale = false;

uniform vec4 inputSize;

float pattern() {
  float s = sin(uAngle), c = cos(uAngle);
  vec2 tex = vTextureCoord * inputSize.xy;
  vec2 point = vec2(
  c * tex.x - s * tex.y,
  s * tex.x + c * tex.y
  ) * uScale;
  return (sin(point.x) * sin(point.y)) * 4.0;
}

void main() {
  vec4 color = texture2D(uSampler, vTextureCoord);
  vec3 colorRGB = vec3(color);

  if (uGrayScale) {
  colorRGB = vec3(color.r + color.g + color.b) / 3.0;
  }

  gl_FragColor = vec4(colorRGB * 10.0 - 5.0 + pattern(), color.a);
}

Built-in Effects

The SDK ships with a large collection of built-in effects. They are registered automatically on the Library when the Engine starts and are ready to be attached to any clip.

EffectIDDescription
Adjustmentbuiltin-adjustmentBrightness, contrast, saturation, hue, vibrance, gamma, temperature, tint.
Advanced Bloombuiltin-advanced-bloomBloom with threshold, scale, and brightness.
ASCIIbuiltin-asciiASCII art stylization.
Bevelbuiltin-bevel3D bevel effect.
Bloombuiltin-bloomSimple bloom.
Blurbuiltin-blurGaussian blur.
Bulge Pinchbuiltin-bulge-pinchBulge or pinch distortion.
Chroma Keybuiltin-chroma-keyGreen / blue screen removal with luminosity influence.
Color Overlaybuiltin-color-overlaySolid color overlay.
Color Replacementbuiltin-color-replacementReplace one color with another.
Cross Hatchbuiltin-cross-hatchHand-drawn cross-hatch pattern.
CRTbuiltin-crtCRT monitor scanlines and curvature.
Dotbuiltin-dotDot screen pattern.
Drop Shadowbuiltin-drop-shadowDrop shadow with color, blur, distance, angle, and alpha.
Embossbuiltin-embossEmboss effect.
Glowbuiltin-glowGlow effect with strength and radius.
Godraybuiltin-godrayVolumetric light shafts.
HSL Adjustmentbuiltin-hsl-adjustmentHSL color controls.
Motion Blurbuiltin-motion-blurDirectional motion blur.
Noisebuiltin-noiseFilm grain / noise overlay.
Old Filmbuiltin-old-filmVintage film look.
Outlinebuiltin-outlineEdge detection outline.
Pixelatebuiltin-pixelatePixelation with configurable block size.
Radial Blurbuiltin-radial-blurRadial blur from a center point.
Reflectionbuiltin-reflectionReflection / mirror effect.
RGB Splitbuiltin-rgb-splitChromatic aberration.
Shockwavebuiltin-shockwaveAnimated shockwave distortion.
Tilt Shiftbuiltin-tilt-shiftTilt-shift simulated miniature effect.
Twistbuiltin-twistTwist / swirl distortion.
Zoom Blurbuiltin-zoom-blurRadial zoom blur.

Try a Few Effects

Blur

The blur radius is controlled by strength.

typescript
const shape = await layer.addClip({
  type: "shape",
  shape: "rectangle",
  startTime: 0,
  duration: 5,
  style: { fillColor: "#6d6ff2", width: 600, height: 300 },
});
shape.style.setPosition(960, 540);

shape.addEffect("builtin-blur", { strength: 16 });
Idle

Pixelate

Low values produce chunky pixels; high values flatten the image.

typescript
const mediaId = await Engine.getInstance().getLibrary().addMedia("https://images.pexels.com/photos/24253539/pexels-photo-24253539/free-photo-of-a-bridge-over-a-river-with-a-city-in-the-background.jpeg?auto=compress&cs=tinysrgb&w=1600");
const clip = await layer.addClip({
  mediaDataId: mediaId,
  startTime: 0,
  duration: 5,
});
clip.style.setPosition(960, 540);
clip.style.setScale(0.6, 0.6);

clip.addEffect("builtin-pixelate", { size: [24, 24] });
Idle

Drop Shadow

A drop shadow underneath the clip, offset by a [x, y] vector in pixels.

typescript
const shape = await layer.addClip({
  type: "shape",
  shape: "rectangle",
  startTime: 0,
  duration: 5,
  style: { fillColor: "#6d6ff2", width: 500, height: 300 },
});
shape.style.setPosition(960, 540);

shape.addEffect("builtin-drop-shadow", {
  color: [1, 0, 0],
  alpha: 0.6,
  blur: 8,
  offset: [10, 10],
});
Idle

Adjustment

Color-correct on the fly: brightness, contrast, saturation, vibrance, temperature, tint. Every field defaults to its neutral value (1 for multipliers, 0 for additive controls), so only override what you care about.

typescript
const mediaId = await Engine.getInstance().getLibrary().addMedia("https://images.pexels.com/photos/24253539/pexels-photo-24253539/free-photo-of-a-bridge-over-a-river-with-a-city-in-the-background.jpeg?auto=compress&cs=tinysrgb&w=1600");
const clip = await layer.addClip({
  mediaDataId: mediaId,
  startTime: 0,
  duration: 5,
});
clip.style.setPosition(960, 540);
clip.style.setScale(0.6, 0.6);

clip.addEffect("builtin-adjustment", {
  brightness: 1.05, // default 1
  contrast: 1.3, // default 1
  saturation: 1.4, // default 1
  temperature: -0.4, // default 0; negative = cooler
  vibrance: 0.8, // default 0
});
Idle

RGB Split

A chromatic-aberration look. Separates the red/green/blue channels.

typescript
const clip = await layer.addClip({
  type: "text",
  text: "Glitch",
  startTime: 0,
  duration: 5,
  style: { fontSize: 200, color: "#FFFFFF" },
});
clip.style.setPosition(960, 540);

clip.addEffect("builtin-rgb-split", {
  red: [-10, 0],
  green: [0, 0],
  blue: [10, 0],
});
Idle

Applying by ID

Any built-in effect can be applied by its id:

typescript
clip.addEffect("builtin-blur", { strength: 8 });

You can also retrieve the full list of built-in IDs at runtime:

typescript
const ids = Engine.getInstance().getLibrary().getBuiltInEffectIds();

INFO

If you self-host the SDK assets, make sure the sdk/assets/effects_v2 folder is in sync with your version. New effects like noise require updated assets.

Adding Custom Effects

Before applying a custom effect to a clip, you must first add it to the Library:

typescript
import { Engine } from "@rendley/sdk";

const libraryEffectId = await Engine.getInstance().getLibrary().addEffect({
  id: "randomId",
  name: "Random Effect",
  fragmentSrc, // GLSL shader code
  serializable: true,
  properties: {}, // A dictionary of uniforms that can be used in the fragment shader.
});

INFO

The serializable property determines whether the effect will be included in the serialized state of the project. If set to false, you'll need to re-load the effect using the onSetupLibrary callback during project initialization.

Once added to the Library, you can apply the effect to a clip:

typescript
clip.addEffect(libraryEffectId);

You can also pass initial values for uniforms when applying the effect:

typescript
clip.addEffect(libraryEffectId, {
  uAngle: 5.0,
});

Updating Effect Properties

Each effect on a clip is an instance that can be updated independently:

typescript
const effect = clip.getEffects()[0];

effect.setProperty("uAngle", 2.0);
const value = effect.getProperty("uAngle");

Property changes are undoable (see Undo / Redo) and can also be animated with the Property Animator using the effect:{effectInstanceId}:{propertyName} track key.

Removing Effects

To remove an effect, call the removeEffect method with the ID of the effect instance on the clip:

typescript
clip.removeEffect(effectInstanceId);

Built-in Uniforms

You can use several built-in uniforms in your effects, including:

UniformTypeDescription
uTimefloatTime in seconds since the clip's startTime
uSamplersampler2DThe source texture to which the effect is being applied.
outputFramevec4The output frame's x, y, width, and height in pixels.
inputSizevec4Dimensions of the input texture: (width, height, 1/width, 1/height).
resolutionvec2The resolution is the ratio of screen (CSS) pixels to real pixels.
inputPixelvec4Pixel size of the input: (1/width, 1/height, width, height).
inputClampvec4Clamping boundaries for the input texture to prevent sampling outside bounds.

You can learn more about these uniforms here.