# How to Add Custom Video Effects to Clypra: A Complete Guide to the Effect Renderer System

> Learn to add custom video effects to Clypra by extending the EffectRenderer. This guide details creating parameters, implementing drawing logic, and UI registration for seamless integration.

- Repository: [Abdulkabir Musa/Clypra](https://github.com/AIEraDev/Clypra)
- Tags: how-to-guide
- Published: 2026-07-16

---

**To add custom video effects in Clypra, you define a TypeScript interface for your parameters, extend the `EffectRenderer` base class to implement the drawing logic, and register the effect in the UI registry.**

Clypra’s video effects subsystem is built around an extensible type system and a runtime **EffectRenderer** class that draws effects onto the canvas. Whether you want to implement glitch effects, color grading, or custom shaders, the architecture follows a consistent three-step pattern that integrates seamlessly with the playback pipeline.

## Understanding Clypra's Effect Architecture

The effect system consists of three core layers that work together at runtime. First, the **data contract layer** ([`src/features/video-effects/types.ts`](https://github.com/AIEraDev/Clypra/blob/main/src/features/video-effects/types.ts)) defines what parameters your effect accepts. Second, the **rendering layer** ([`src/features/video-effects/renderers/EffectRenderer.ts`](https://github.com/AIEraDev/Clypra/blob/main/src/features/video-effects/renderers/EffectRenderer.ts)) handles the actual pixel manipulation using Canvas2D or WebGL contexts. Third, the **integration layer** ([`src/features/video-effects/index.ts`](https://github.com/AIEraDev/Clypra/blob/main/src/features/video-effects/index.ts) and UI components) exposes the effect to users and wires it into the timeline.

When the playback engine renders a frame, it calls `applyRendererEffect` from [`src/features/video-effects/utils/applyRendererEffect.ts`](https://github.com/AIEraDev/Clypra/blob/main/src/features/video-effects/utils/applyRendererEffect.ts), which instantiates the appropriate renderer subclass and passes the canvas context and parameters.

## Step 1 – Define the Effect Data Contract

Every custom effect starts with a TypeScript interface that describes its configurable parameters. This interface becomes part of the union type that the rest of the application uses for type safety.

### Adding Your Interface to types.ts

Open [`src/features/video-effects/types.ts`](https://github.com/AIEraDev/Clypra/blob/main/src/features/video-effects/types.ts) and add your effect definition to the existing union. Ensure you export the interface so renderers and UI components can import it.

```typescript
// src/features/video-effects/types.ts
export interface GlitchEffect {
  type: 'glitch';
  intensity: number;   // 0-1 range
  offset: number;      // pixels to shift
}

export type VideoEffect = GlitchEffect | /* existing effects */;

```

The `type` property serves as a discriminant for the union, allowing the TypeScript compiler to narrow types correctly throughout the application.

## Step 2 – Implement the Rendering Logic

The rendering implementation extends the abstract `EffectRenderer` base class and overrides the `apply` method to execute your custom drawing code.

### Extending EffectRenderer

Create a new file in `src/features/video-effects/renderers/` that inherits from `EffectRenderer`. The base class provides lifecycle management, while your subclass handles the specific visual transformation.

Here is a complete implementation of a Glitch effect renderer:

```typescript
// src/features/video-effects/renderers/GlitchRenderer.ts
import { EffectRenderer } from './EffectRenderer';
import type { GlitchEffect } from '../types';

export class GlitchRenderer extends EffectRenderer {
  apply(
    ctx: CanvasRenderingContext2D,
    params: GlitchEffect,
    time: number
  ): void {
    const { intensity, offset } = params;
    const w = ctx.canvas.width;
    const h = ctx.canvas.height;

    // Simple horizontal offset glitch based on time
    const shift = Math.sin(time * 30) * offset * intensity;
    ctx.drawImage(ctx.canvas, shift, 0, w, h, 0, 0, w, h);
  }
}

```

### The apply Method Signature

The `apply` method receives three arguments: the **canvas rendering context** (`CanvasRenderingContext2D` or WebGL context), the **effect parameters** (typed via your interface from Step 1), and the **current time** in seconds. You must respect this signature exactly, as the playback pipeline depends on it when dispatching effects.

### Registering in applyRendererEffect.ts

After implementing the renderer, map the effect type to your class in [`src/features/video-effects/utils/applyRendererEffect.ts`](https://github.com/AIEraDev/Clypra/blob/main/src/features/video-effects/utils/applyRendererEffect.ts):

```typescript
// src/features/video-effects/utils/applyRendererEffect.ts
import { GlitchRenderer } from '../renderers/GlitchRenderer';

export const rendererMap = {
  glitch: GlitchRenderer,
  // ... other effect mappings
};

```

This registry allows the runtime to instantiate the correct renderer when it encounters your effect type in the timeline.

## Step 3 – Expose the Effect in the UI

Users interact with effects through the interface components located in `src/features/video-effects/components/`. You must register your effect in the central index and update the picker component.

### Registering in the Effect Registry

Open [`src/features/video-effects/index.ts`](https://github.com/AIEraDev/Clypra/blob/main/src/features/video-effects/index.ts) and add your effect to the registry, providing the renderer class and default parameters:

```typescript
// src/features/video-effects/index.ts
import { GlitchRenderer } from './renderers/GlitchRenderer';

export const effectRegistry = {
  glitch: {
    name: 'Glitch',
    renderer: GlitchRenderer,
    defaultParams: { 
      type: 'glitch', 
      intensity: 0.5, 
      offset: 10 
    },
  },
  // ... existing entries
};

```

This registry entry ties the technical implementation to the user-facing name and initial values.

### Adding UI Components

Update [`src/features/video-effects/components/EffectPicker.tsx`](https://github.com/AIEraDev/Clypra/blob/main/src/features/video-effects/components/EffectPicker.tsx) to include your effect in the selection list. The component typically iterates over `effectRegistry` to populate the dropdown or panel:

```typescript
// src/features/video-effects/components/EffectPicker.tsx
{Object.entries(effectRegistry).map(([key, def]) => (
  <option key={key} value={key}>
    {def.name}
  </option>
))}

```

For advanced parameter controls, create a custom panel in [`src/features/video-effects/components/EffectsPanel.tsx`](https://github.com/AIEraDev/Clypra/blob/main/src/features/video-effects/components/EffectsPanel.tsx) that binds input fields to the effect's parameters. The component receives the active effect's parameter object and updates it via the application's state management.

## How the Runtime Pipeline Works

When a clip with your custom effect plays, Clypra executes the following sequence:

1. **Timeline Lookup**: The playback engine identifies that the current clip has a video effect attached, retrieving the effect ID and parameter blob from the timeline state.
2. **Renderer Dispatch**: The engine calls `applyEffect` from [`src/features/video-effects/utils/applyEffect.ts`](https://github.com/AIEraDev/Clypra/blob/main/src/features/video-effects/utils/applyEffect.ts), which consults the `rendererMap` to instantiate the appropriate `EffectRenderer` subclass.
3. **Frame Rendering**: Your renderer's `apply` method receives the canvas context, current parameters, and playback time. After executing your drawing code, the modified frame proceeds to the next effect or displays to the user.

Because `EffectRenderer` is a standard TypeScript class, you can utilize any Canvas2D API methods, WebGL shaders, or third-party graphics libraries, provided they are bundled with the application.

## Summary

- **Define the contract**: Add a TypeScript interface to [`src/features/video-effects/types.ts`](https://github.com/AIEraDev/Clypra/blob/main/src/features/video-effects/types.ts) describing your effect's parameters.
- **Implement the renderer**: Extend `EffectRenderer` in [`src/features/video-effects/renderers/EffectRenderer.ts`](https://github.com/AIEraDev/Clypra/blob/main/src/features/video-effects/renderers/EffectRenderer.ts), override the `apply` method, and register the class in [`applyRendererEffect.ts`](https://github.com/AIEraDev/Clypra/blob/main/applyRendererEffect.ts).
- **Expose to users**: Register the effect in [`src/features/video-effects/index.ts`](https://github.com/AIEraDev/Clypra/blob/main/src/features/video-effects/index.ts) with default parameters, and add it to [`EffectPicker.tsx`](https://github.com/AIEraDev/Clypra/blob/main/EffectPicker.tsx) for UI accessibility.
- **Respect the signature**: The `apply(ctx, params, time)` method must match the expected signature to integrate with the playback pipeline.

## Frequently Asked Questions

### What parameters does the apply method receive in Clypra's EffectRenderer?

The `apply` method receives three parameters: a `CanvasRenderingContext2D` (or WebGL context) for drawing, a params object typed to your specific effect interface, and a number representing the current playback time in seconds. This signature is strictly enforced by the base class in [`src/features/video-effects/renderers/EffectRenderer.ts`](https://github.com/AIEraDev/Clypra/blob/main/src/features/video-effects/renderers/EffectRenderer.ts).

### Can I use WebGL instead of Canvas2D for custom video effects?

Yes. While the examples show `CanvasRenderingContext2D`, the `EffectRenderer` base class can accept a WebGL rendering context. Ensure your subclass is typed to receive `WebGL2RenderingContext` or `WebGLRenderingContext`, and implement your shaders within the `apply` method using standard WebGL APIs.

### Where do I register a new video effect so it appears in the Clypra editor?

You must register the effect in two locations: first, add the renderer mapping to [`src/features/video-effects/utils/applyRendererEffect.ts`](https://github.com/AIEraDev/Clypra/blob/main/src/features/video-effects/utils/applyRendererEffect.ts) so the runtime can instantiate it; second, add the metadata entry to [`src/features/video-effects/index.ts`](https://github.com/AIEraDev/Clypra/blob/main/src/features/video-effects/index.ts) so the UI components in [`EffectPicker.tsx`](https://github.com/AIEraDev/Clypra/blob/main/EffectPicker.tsx) can display it to users.

### How does Clypra handle multiple effects on the same video clip?

The playback pipeline processes effects sequentially. For each frame, `applyEffect` iterates through the active effects list, calling the corresponding renderer's `apply` method for each one. The canvas context persists between calls, allowing each effect to build upon the previous one's output, creating a composited result.