Factory.ai

Open-Source Wikis

/

Stable Diffusion WebUI

/

Systems

/

Scripts and extensions

AUTOMATIC1111/stable-diffusion-webui

Scripts and extensions

Active contributors: AUTOMATIC1111, w-e-w, missionfloyd, light-and-ray, Aarni Koskela

Purpose

Implements the plugin system that lets third-party code add UI tabs, mutate generations, register settings, and wire into nearly every step of the processing pipeline. There are two related concepts:

  • Scripts — Python files placed under scripts/ (in the repo) or extensions/<name>/scripts/. Each defines a Script subclass.
  • Extensions — directories under extensions/ (third-party) or extensions-builtin/. An extension is more than scripts: it can ship its own metadata.ini, dependencies, JS/CSS, and install.py. The Lora system is the largest extension shipped in-repo.

Directory layout

modules/
├── scripts.py                   # Script base class + ScriptRunner + load_scripts()
├── scripts_postprocessing.py    # ScriptPostprocessing for Extras tab
├── scripts_auto_postprocessing.py
├── extensions.py                # Extension dataclass + scanner
├── script_loading.py            # safe importlib wrapper
├── script_callbacks.py          # the named hook system (separate page)
└── ui_extensions.py             # the Extensions tab UI
extensions-builtin/              # ships in repo: Lora, LDSR, SwinIR, ScuNET, hypertile, …
extensions/                      # gitignored; user installs go here
scripts/                         # one-shot scripts (X/Y/Z, prompt matrix, outpainting, etc.)
modules/processing_scripts/      # alwayson built-ins (refiner, comments, sampler, seed)

Key abstractions

Type File Description
Script modules/scripts.py Base class. Subclasses implement title(), show(is_img2img), ui(is_img2img), and any of the lifecycle hooks below.
ScriptBuiltinUI same Variant for the built-ins under processing_scripts/ whose UI is part of the standard tab layout.
ScriptWithDependencies same Optional base for scripts that declare other scripts they need. Used for ordering.
ScriptRunner same One per tab (txt2img, img2img, extras); owns the active scripts list and drives them.
AlwaysVisible same Sentinel returned from Script.show() to mark an alwayson script.
Extension modules/extensions.py Per-directory metadata: name, path, branch, commit hash, requires, version.
ExtensionMetadata same Reads metadata.ini for [Extension] Name, Requires, and [callbacks/...] ordering blocks.
load_scripts() modules/scripts.py Boot-time function: imports every script, instantiates each Script subclass once, and groups them by tab.

Script lifecycle

A Script subclass's hooks are called in this order during a generation:

sequenceDiagram
    participant SR as ScriptRunner
    participant S as Script
    participant P as processing.py

    P->>SR: process(p)
    SR->>S: process(p, *args)
    P->>SR: before_process_batch(p, **kwargs)
    SR->>S: before_process_batch
    P->>SR: process_batch(p, **kwargs)
    SR->>S: process_batch
    Note over S: per-batch / per-step hooks
    P->>SR: process_before_every_sampling(p, x, noise, c, uc)
    SR->>S: process_before_every_sampling (v1.10)
    Note over P,S: sample loop runs (callbacks fire too)
    P->>SR: postprocess_batch(p, images, **kwargs)
    SR->>S: postprocess_batch
    P->>SR: postprocess_image(p, pp)
    SR->>S: postprocess_image
    P->>SR: postprocess(p, processed, **kwargs)
    SR->>S: postprocess

Other relevant hooks:

Hook Purpose
setup(p, *args) Called once per generation request, before any models are loaded.
before_process(p, *args) Right before process(); used by scripts that need to switch models early.
before_hr(p, *args) Called between the low-res and high-res passes of hires fix.
postprocess_image_after_composite(p, pp) After mask composition for img2img.
postprocess_maskoverlay(p, mask_for_overlay, overlay_image, index) Customise the mask overlay used in inpainting.
on_before_component(*, component, **kwargs) / on_after_component(...) Hook into UI construction by elem_id.

The args passed through *args come from the script's own UI controls — ScriptRunner slices the global Gradio inputs list using each script's args_from/args_to indices.

Alwayson vs one-shot scripts

Script.show(is_img2img) controls visibility:

  • True — the script is selectable in the Script dropdown and only runs when chosen (X/Y/Z plot, prompt matrix, outpainting). Their UI appears below the dropdown when selected.
  • False — never shown. Used for legacy scripts.
  • AlwaysVisible — the script's UI is always rendered as a section on the tab and its hooks always fire (refiner, ControlNet, ADetailer). This is by far the more common pattern for extensions.

Boot

graph TD
    init[initialize.initialize] --> load[scripts.load_scripts]
    load --> walk[walk extensions-builtin/ + extensions/ + scripts/]
    walk --> import[script_loading.load_module]
    import --> filter[find Script / ScriptPostprocessing subclasses]
    filter --> instantiate[instantiate one of each]
    filter --> sort[topological_sort by ExtensionMetadata.requires +<br/>callback Before/After ordering]
    sort --> register[scripts_data + postprocessing_scripts_data globals]
    register --> tabs[ScriptRunner per tab uses these globals]

load_scripts() is in modules/scripts.py. It walks four locations:

  1. scripts/ (repo built-in one-shots)
  2. modules/processing_scripts/ (repo built-in alwayson)
  3. extensions-builtin/<name>/scripts/ (in-repo extensions)
  4. extensions/<name>/scripts/ (user-installed extensions)

A failure in one script reports an error via errors.report but doesn't stop other scripts from loading. The order is determined by the topological sort over ExtensionMetadata.requires plus per-callback Before/After hints from metadata.ini.

Extensions tab

modules/ui_extensions.py provides the Extensions tab. It can:

  • List installed extensions with their git commit info (read by Extension.read_info_from_repo() in a thread).
  • Install new extensions from a URL or from the Available list (a JSON index downloaded from https://raw.githubusercontent.com/AUTOMATIC1111/stable-diffusion-webui-extensions/master/index.json).
  • Update extensions via git pull — the wrapper gitpython_hack.py exists because GitPython's defaults break on Windows path lengths.
  • Disable extensions by writing to config_states.json. Disabled extensions are skipped during the next boot.

The Extensions tab is heavily controlled by command-line flags: --enable-insecure-extension-access is required on hosted/shared deployments, and --disable-all-extensions (or disable_all_extensions=all in opts) disables the entire system.

Extension manifest (metadata.ini)

Optional but recommended. Format:

[Extension]
Name = my-extension
Requires = sd-webui-controlnet, ultimate-upscale

[callbacks/cfg_denoiser]
Before = a1111-controlnet
After  = adetailer

Requires ensures dependent extensions load first. [callbacks/<name>] blocks let the extension declare ordering for individual callbacks — useful when several extensions hook the same step.

Postprocessing scripts (Extras tab)

The Extras tab uses a different base class: ScriptPostprocessing from modules/scripts_postprocessing.py. Each registered postprocessor declares name, order, and process(pp, **args); the Extras tab runs them in order on each input image. Built-ins ship under scripts/postprocessing_*.py (Codeformer, GFPGAN, upscale).

Integration points

  • Add UI controls to a tab — implement Script.ui(is_img2img). The returned components flow through *args into your hook methods.
  • Mutate p before sampling — implement process(p).
  • Modify the noise prediction at every step — register a cfg_denoiser callback (see script-callbacks.md) rather than implementing it as a script hook; cleaner and faster.
  • Add a settings entry — register on_ui_settings. Don't write directly to shared_options.py.
  • Add a custom tabon_ui_tabs.
  • Read PNG metadata into your UI — set paste_field_names = ["My Setting"] and infotext_fields = [(component, "My Setting"), ...] on the script.

Entry points for modification

  • A change to the Script class API is likely a breaking change for hundreds of extensions. Add new optional hooks; never reorder or remove existing ones.
  • The extensions-builtin/Lora/ subtree is the largest in-repo example of a fully-fledged extension; treat it as the reference implementation when reasoning about ordering, settings registration, and metadata.ini.
  • The script registration bug surface is documented in report_exception calls scattered through scripts.py — anything that raises in a hook is caught and printed but doesn't abort generation.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Scripts and extensions – Stable Diffusion WebUI wiki | Factory