# How AUTOMATIC1111 Extensions and Scripts Work: A Complete Development Guide

> Learn how AUTOMATIC1111 extensions and scripts work to add new features to Stable Diffusion. Develop your own custom tools with this complete guide.

- Repository: [AUTOMATIC1111/stable-diffusion-webui](https://github.com/AUTOMATIC1111/stable-diffusion-webui)
- Tags: how-to-guide
- Published: 2026-02-24

---

**AUTOMATIC1111 extensions are optional packages loaded from the `extensions/` directory that add UI tabs and models, while scripts are Python plugins subclassing `modules.scripts.Script` that inject controls into txt2img/img2img; both are auto-discovered at runtime via [`modules/extensions.py`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/modules/extensions.py) and [`modules/scripts.py`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/modules/scripts.py) using [`metadata.ini`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/metadata.ini) for dependency management.**

The AUTOMATIC1111/stable-diffusion-webui repository provides a modular plugin system allowing developers to extend the Stable Diffusion interface without modifying core code. Understanding how **AUTOMATIC1111 extensions and scripts** function enables you to add custom processing pipelines, new interface tabs, and specialized post-processing effects. This guide examines the exact loading mechanisms in [`modules/extensions.py`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/modules/extensions.py) and [`modules/scripts.py`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/modules/scripts.py) to show you how to build and register your own components.

## Understanding the Extension Architecture

Extensions in the WebUI are self-contained packages that live in the `extensions/` folder (or the built-in `extensions-builtin/`). The core loader in [`modules/extensions.py`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/modules/extensions.py) maintains global lists named `extensions` and `extension_paths` that track every discovered package.

### How Extensions Are Discovered

When the application starts, `list_extensions()` in [`modules/extensions.py`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/modules/extensions.py) scans both `extensions-builtin/` and `extensions/` directories. For each folder found:

1. It instantiates `ExtensionMetadata` by reading the [`metadata.ini`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/metadata.ini) file, parsing fields like `Name`, `Requires`, `Before`, and `After`.
2. It creates an `Extension` object storing the path, repository information, and enabled flag.
3. It validates dependencies—if an extension lists `Requires = another-extension` and that dependency is missing or disabled, the loader aborts loading the dependent extension.

The UI renders these packages in the Extensions tab via [`modules/ui_extensions.py`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/modules/ui_extensions.py), where users can toggle enable flags and apply changes to trigger a reload.

## How Scripts Integrate with the UI

While extensions add broad functionality, **scripts** provide granular control within the generation pipeline. They are defined in [`modules/scripts.py`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/modules/scripts.py) and implement the `Script` base class.

### The Script Loading Pipeline

The `load_scripts()` function orchestrates discovery through this exact sequence:

1. **Enumeration**: `list_scripts("scripts", ".py")` first scans the core `scripts/` folder, then appends any `scripts/` sub-folders found inside active extensions using `ext.list_files`.
2. **Dependency Resolution**: For each script file, the system creates a `ScriptWithDependencies` object. It reads `requires`, `load_before`, and `load_after` from the parent extension's [`metadata.ini`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/metadata.ini).
3. **Topological Sorting**: The loader calls `util.topological_sort` to resolve the load order, ensuring scripts with dependencies load after their requirements while preventing circular references.
4. **Instantiation**: The ordered list of `ScriptFile` objects is imported via `script_loading.load_module`. Every class subclassing `modules.scripts.Script` is instantiated and stored in `scripts_data` or `postprocessing_scripts_data`.

### Script Execution and Callbacks

The `ScriptRunner` class in [`modules/scripts.py`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/modules/scripts.py) manages runtime behavior. When creating the UI, `create_script_ui` records argument index ranges (`args_from`, `args_to`) so the runner can slice `p.script_args` when invoking callbacks. Scripts can implement several lifecycle methods:

- `run(self, p, *args)` – Executed when the user selects the script from the dropdown and clicks Generate.
- `before_process(self, p, *args)` – Called before the diffusion process begins; allows modifying prompts or parameters.
- `process(self, p, *args)` – Called during processing.
- `postprocess(self, p, processed, *args)` – Called after image generation; allows modifying the final `processed` object.

For **Always-on** scripts that remain visible in the UI regardless of dropdown selection, implement `show(self, is_img2img)` to return `scripts.AlwaysVisible`.

## Creating a Custom Extension

To create a new extension, you need a folder with a [`metadata.ini`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/metadata.ini) file and optional Python modules.

### Required Folder Structure

Create a directory under `extensions/my-cool-ext/` with the following layout:

```

extensions/
└─ my-cool-ext/
   ├─ metadata.ini
   ├─ ui.py               # optional: adds a new tab

   └─ scripts/
       └─ hello_world.py  # optional: custom scripts

```

### The metadata.ini File

The [`metadata.ini`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/metadata.ini) file is mandatory for the loader to recognize your package. Place it in the extension root:

```ini
[Extension]
Name = My Cool Extension
Requires = another-extension
Before = some-extension
After = other-extension

```

- **Name**: Display name in the Extensions tab.
- **Requires**: Comma-separated list of extension names that must be present and enabled.
- **Before/After**: Hints for load order relative to other extensions.

### Adding a UI Tab

To register a new Gradio tab, create [`ui.py`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/ui.py) in your extension folder:

```python

# extensions/my-cool-ext/ui.py

import gradio as gr
from modules import ui_extensions

def tab_content():
    with gr.Column():
        gr.Markdown("## My Extension Tab")

        gr.Button("Execute", elem_id="my_btn")

# Register when the extension loads

ui_extensions.register_ui_tab(name="My Cool Extension", func=tab_content)

```

## Creating a Custom Script

Scripts live in the `scripts/` directory (either the root `scripts/` folder or inside an extension's `scripts/` sub-folder). Each script must define a class inheriting from `modules.scripts.Script`.

### Basic Script Example

Create [`scripts/hello_world.py`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/scripts/hello_world.py):

```python
from modules import scripts, shared
import gradio as gr

class HelloWorld(scripts.Script):
    """Adds a text input and logs messages during generation."""

    def title(self):
        return "Hello World"

    def ui(self, is_img2img):
        # Components appear when this script is selected

        self.msg = gr.Textbox(label="Message", value="Hello from script!")
        return [self.msg]

    def run(self, p, msg):
        # p is the StableDiffusionProcessing object

        shared.log.info(f"[HelloWorld] User message: {msg}")
        # Return None to continue with normal generation

        return None

```

The `title()` method determines the dropdown label. The `ui()` method returns a list of Gradio components whose values are passed as arguments to `run()`.

### Always-On Script Example

For scripts that should always appear in the interface:

```python

# scripts/always_on_example.py

from modules import scripts
import gradio as gr

class AlwaysOnExample(scripts.Script):
    def title(self):
        return "Always-On Example"

    def show(self, is_img2img):
        # Makes UI always visible

        return scripts.AlwaysVisible

    def ui(self, is_img2img):
        self.factor = gr.Slider(0.1, 3.0, step=0.1, label="Scale Factor")
        return [self.factor]

    def before_process(self, p, factor):
        # Modify the prompt before generation

        p.prompt = f"{p.prompt} ++scale:{factor}"

```

### Post-Processing Script Example

To manipulate images after generation:

```python

# scripts/invert_colors.py

from modules import scripts, shared
from PIL import Image
import numpy as np
import gradio as gr

class InvertColors(scripts.Script):
    def title(self):
        return "Invert Colors"

    def show(self, is_img2img):
        return scripts.AlwaysVisible

    def ui(self, is_img2img):
        self.enable = gr.Checkbox(label="Enable Inversion", value=False)
        return [self.enable]

    def postprocess(self, p, processed, enable):
        if not enable:
            return
        # processed.images is a list of PIL Images

        processed.images = [
            Image.fromarray(255 - np.array(img)) 
            for img in processed.images
        ]
        shared.log.info("[InvertColors] Colors inverted")

```

## Managing Dependencies and Load Order

Both extensions and scripts use the same dependency system defined in [`metadata.ini`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/metadata.ini).

### Dependency Resolution

When [`modules/extensions.py`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/modules/extensions.py) parses [`metadata.ini`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/metadata.ini), it validates the `Requires` field to ensure dependencies exist and are enabled. For scripts, the loader reads these same keys from their parent extension's metadata to build the `ScriptWithDependencies` graph.

### Load Order Control

The `Before` and `After` keys in [`metadata.ini`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/metadata.ini) determine initialization sequence. The loader passes these constraints to `util.topological_sort`, which produces a deterministic load order. This prevents initialization errors when one script patches functionality that another script depends on.

If a circular dependency is detected, the loader raises an error and prevents the UI from starting, ensuring system stability.

## Summary

- **Extensions** are directory-based packages discovered by [`modules/extensions.py`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/modules/extensions.py) via `list_extensions()`, requiring a [`metadata.ini`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/metadata.ini) file to declare metadata and dependencies.
- **Scripts** are Python classes subclassing `modules.scripts.Script`, loaded by [`modules/scripts.py`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/modules/scripts.py) using topological sorting to resolve `Requires`, `Before`, and `After` constraints.
- The `ScriptRunner` class manages script lifecycle, injecting Gradio UI components and routing arguments to callbacks like `run()`, `before_process()`, and `postprocess()`.
- Always-on scripts return `scripts.AlwaysVisible` from `show()` to remain persistently visible in the txt2img/img2img interface.
- Extensions can add new UI tabs by calling `ui_extensions.register_ui_tab()` in a [`ui.py`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/ui.py) file within the extension folder.

## Frequently Asked Questions

### What is the difference between an extension and a script in AUTOMATIC1111?

An **extension** is a complete package that may contain multiple scripts, UI tabs, models, and JavaScript, defined by a folder with [`metadata.ini`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/metadata.ini). A **script** is a single Python file containing a `Script` subclass that appears in the Scripts dropdown or as an always-on panel. Extensions are managed at the repository level, while scripts are managed individually within the generation interface.

### Where should I place my custom script files?

Place standalone scripts in the root `scripts/` directory. If your script belongs to a specific extension, place it inside that extension's `scripts/` sub-folder (e.g., [`extensions/my-ext/scripts/my_script.py`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/extensions/my-ext/scripts/my_script.py)). The loader in [`modules/scripts.py`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/modules/scripts.py) automatically discovers both locations when `load_scripts()` runs at startup.

### How do I make my script always visible in the interface?

Override the `show(self, is_img2img)` method in your `Script` subclass to return `scripts.AlwaysVisible` instead of the default `True`. This causes the Gradio components from your `ui()` method to appear persistently in the txt2img or img2img panels, allowing users to toggle features without selecting the script from the dropdown.

### Can my extension depend on another extension being installed?

Yes. In your extension's [`metadata.ini`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/metadata.ini), add a `Requires` key listing the required extension names separated by commas (e.g., `Requires = controlnet, sd-webui-deforum`). The loader validates these dependencies during `list_extensions()` in [`modules/extensions.py`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/main/modules/extensions.py) and will disable your extension if requirements are missing, logging the conflict to the console.