> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pixelatestudio.tomblack.ca/llms.txt
> Use this file to discover all available pages before exploring further.

# Extension authoring

> Write your own Pixelate Pro effect: settings, validation, pixel processing and assembly setup.

You can add your own effects to Pixelate Pro. A custom effect appears in the **Add Effect** menu next to the built-in ones, works with Style Profiles and per-asset overrides, and runs in both the Preview and Export.

This page is for programmers. You need to be comfortable writing Unity editor C# and assembly definitions.

## How an effect works

An effect is a `ScriptableObject` class that inherits `PixelateEffect`. It has three jobs:

1. **Hold settings** as serialized fields. Pixelate Pro draws them in the Inspector, with override checkboxes in Pixelate Assets.
2. **Validate** its settings and report problems the user can act on.
3. **Process pixels** of each frame, deterministically.

It must not touch the scene, edit the source's renderers or materials, or write files. Pixelate Pro handles capture, preview and export.

## Set up an assembly

Put your code outside the Pixelate Pro folder, so updates to Pixelate Pro never overwrite it:

```text theme={null}
Assets/YourStudio/PixelateEffects/
  YourStudio.PixelateEffects.asmdef
  PosterizeEffect.cs
```

```json YourStudio.PixelateEffects.asmdef theme={null}
{
  "name": "YourStudio.PixelateEffects",
  "rootNamespace": "YourStudio.PixelateEffects",
  "references": [
    "PixelateStudio.PixelatePro"
  ],
  "includePlatforms": [],
  "excludePlatforms": [],
  "allowUnsafeCode": false,
  "overrideReferences": false,
  "autoReferenced": true
}
```

## A complete effect

This effect reduces each color channel to a few levels.

```csharp PosterizeEffect.cs theme={null}
using System.Collections.Generic;
using UnityEngine;
using PixelateStudio;
using PixelateStudio.Extensions;

namespace YourStudio.PixelateEffects
{
    [PixelateExtensionMenu(
        stableId: "yourstudio.pixelate.posterize",
        displayName: "Posterize",
        categoryPath: PixelateExtensionCategories.Stylization,
        phase: PixelateExtensionPhase.Stylization,
        order: 0,
        description: "Limits each color channel to a few levels.",
        allowMultiple: false)]
    public sealed class PosterizeEffect : PixelateEffect
    {
        private const string LevelsField = "_levels";

        [Tooltip("Number of levels per color channel. Lower values give flatter colors.")]
        [SerializeField, Min(2)]
        private int _levels = 4;

        public override void Validate(List<PixelateValidationMessage> messages)
        {
            if (_levels < 2)
            {
                messages.Add(new PixelateValidationMessage(
                    PixelateValidationSeverity.Error,
                    "Posterize Levels must be at least 2.",
                    "Set Levels to 2 or higher.",
                    "yourstudio.pixelate.posterize",
                    LevelsField));
            }
        }

        public override void ProcessPixels(PixelateEffectPixelContext context)
        {
            // Use the asset's override when it has one, otherwise the profile value.
            var shadow = context.OverrideShadow as PosterizeEffect;
            int levels = shadow != null && shadow.IsFieldOverridden(LevelsField)
                ? shadow._levels
                : _levels;
            levels = Mathf.Max(2, levels);

            Color32[] pixels = context.Pixels;
            float step = 255f / (levels - 1);
            for (int i = 0; i < pixels.Length; i++)
            {
                Color32 p = pixels[i];
                if (p.a == 0)
                    continue;
                p.r = (byte)(Mathf.Round(p.r / step) * step);
                p.g = (byte)(Mathf.Round(p.g / step) * step);
                p.b = (byte)(Mathf.Round(p.b / step) * step);
                pixels[i] = p;
            }
        }
    }
}
```

After Unity compiles, **Posterize** appears in **Add Effect** under Stylization. Adding it from a Pixelate Asset adds it to that asset's Style Profile, because profiles own the effect list.

## The menu attribute

| Field | Required | Default | Purpose |
| - | - | - | - |
| `stableId` | Yes | | Permanent, unique, lowercase id. Used for validation, duplicate detection and support. Don't change it after you ship. |
| `displayName` | Yes | | Name in the Add Effect menu and the effect header. |
| `categoryPath` | Yes | | Menu group. Use a `PixelateExtensionCategories` constant or your own slash-separated path. |
| `phase` | No | `Stylization` | When the effect runs. Effects run by phase, then by `order`. |
| `order` | No | `0` | Order inside the phase. Lower runs first. |
| `description` | No | Empty | Tooltip in the Add Effect menu and on the effect header. |
| `allowMultiple` | No | `false` | Whether a profile can hold more than one instance. |
| `HideInMenu` | No | `false` | Named property. Leaves the effect out of the menu; it still works when added from code. |

If two loaded effects share a `stableId`, Pixelate Pro reports an error naming both types.

### Where your effect runs

Built-in effects use these positions, so you can place yours before or after them:

| Built-in effect | Phase | Order |
| - | - | - |
| Lighting | `Lighting` | 0 |
| Color Adjustments | `Color` | 0 |
| Color Palette | `Color` | 100 |
| Pixel Cleanup | `Stylization` | -100 |
| Dithering | `Stylization` | 0 |
| Lines | `Stylization` | 100 |
| Palette Swap | `Stylization` | 200 |

The Inspector always lists effects in this order, so users see yours where it runs.

## Processing pixels

`ProcessPixels` is called for every Unlit and Lit frame of the Preview, Export and Save GIF, at your effect's position. Normal previews and normal-map exports skip it. Effects that don't override it add no processing step.

`PixelateEffectPixelContext` gives you:

| Member | Description |
| - | - |
| `Pixels` | The frame as `Color32[]`: straight alpha, sRGB values, bottom row first. Modify it in place. Don't keep a reference after the call. |
| `Width`, `Height` | Frame size in pixels. |
| `Asset` | The Pixelate Asset being processed. |
| `OverrideShadow` | The asset's overrides for your effect, or `null`. Read a field from it only when `IsFieldOverridden` returns true for that field. |

Keep processing deterministic: the same input must always give the same output, so the Preview matches Export and re-exports don't change unrelated pixels.

## Validation

Override `Validate` for setup problems the user can fix. Pick the severity by impact:

* **Error:** blocks export. Missing required assets, impossible settings.
* **Warning:** allowed but probably unintended, such as a setting that makes the effect do nothing.
* **Info:** optional guidance.

Always say how to fix the problem in the second argument. Pass your `stableId` and the serialized field name so the message shows next to the right field.

## Inspector

Pixelate Pro draws your serialized fields with Unity's standard property drawers, including `[Tooltip]`, `[Range]` and `[Min]`. Give every field a tooltip. Asset inspectors add the override checkbox for each field automatically.

Don't store foldout or other UI state on the effect; Pixelate Pro keeps that per user in editor preferences.

## Renaming fields after you ship

Per-asset overrides remember fields by name. When you rename a serialized field, add `[FormerlySerializedAs]` with the old name. Pixelate Pro reads it and moves existing overrides to the new name:

```csharp theme={null}
using UnityEngine.Serialization;

[FormerlySerializedAs("_levels")]
[SerializeField, Min(2)]
private int _toneLevels = 4;
```

If an override points at a field that no longer exists, validation reports it with the field name. For removed fields or larger changes, plan how existing overrides migrate, and never reuse an old field name for a different meaning.

## Limits

Only the types used on this page are supported extension API: `PixelateEffect`, `PixelateExtensionMenuAttribute`, `PixelateExtensionCategories`, `PixelateExtensionPhase`, `PixelateEffectPixelContext` and `PixelateValidationMessage`. Other Pixelate Pro types, including custom Inspector drawing and GPU processing, are internal and may change between versions.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.