---
name: mainline-effects
description: How to add new effect plugins to Mainline's effect system
compatibility: opencode
metadata:
  audience: developers
  source_type: codebase
---

## What This Skill Covers

This skill covers Mainline's effect plugin system - how to create, configure, and integrate visual effects into the pipeline.

## Key Concepts

### EffectPlugin ABC (engine/effects/types.py)

All effects must inherit from `EffectPlugin` and implement:

```python
class EffectPlugin(ABC):
    name: str
    config: EffectConfig
    param_bindings: dict[str, dict[str, str | float]] = {}
    supports_partial_updates: bool = False
    
    @abstractmethod
    def process(self, buf: list[str], ctx: EffectContext) -> list[str]:
        """Process buffer with effect applied"""
        ...
    
    @abstractmethod
    def configure(self, config: EffectConfig) -> None:
        """Configure the effect"""
        ...
```

### EffectContext

Passed to every effect's process method:

```python
@dataclass
class EffectContext:
    terminal_width: int
    terminal_height: int
    scroll_cam: int
    ticker_height: int
    camera_x: int = 0
    mic_excess: float = 0.0
    grad_offset: float = 0.0
    frame_number: int = 0
    has_message: bool = False
    items: list = field(default_factory=list)
    _state: dict[str, Any] = field(default_factory=dict)
```

Access sensor values via `ctx.get_sensor_value("sensor_name")`.

### EffectConfig

Configuration dataclass:

```python
@dataclass
class EffectConfig:
    enabled: bool = True
    intensity: float = 1.0
    params: dict[str, Any] = field(default_factory=dict)
```

### Partial Updates

For performance optimization, set `supports_partial_updates = True` and implement `process_partial`:

```python
class MyEffect(EffectPlugin):
    supports_partial_updates = True
    
    def process_partial(self, buf, ctx, partial: PartialUpdate) -> list[str]:
        # Only process changed regions
        ...
```

## Adding a New Effect

1. Create file in `effects_plugins/my_effect.py`
2. Inherit from `EffectPlugin`
3. Implement `process()` and `configure()`
4. Add to `effects_plugins/__init__.py` (runtime discovery via issubclass checks)

## Param Bindings

Declarative sensor-to-param mappings:

```python
param_bindings = {
    "intensity": {"sensor": "mic", "transform": "linear"},
    "rate": {"sensor": "oscillator", "transform": "exponential"},
}
```

Transforms: `linear`, `exponential`, `threshold`

## Effect Chain

Effects are chained via `engine/effects/chain.py` - processes each effect in order, passing output to next.

## Existing Effects

See `effects_plugins/`:
- noise.py, fade.py, glitch.py, firehose.py
- border.py, crop.py, tint.py, hud.py
