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) orextensions/<name>/scripts/. Each defines aScriptsubclass. - Extensions — directories under
extensions/(third-party) orextensions-builtin/. An extension is more than scripts: it can ship its ownmetadata.ini, dependencies, JS/CSS, andinstall.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: postprocessOther 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:
scripts/(repo built-in one-shots)modules/processing_scripts/(repo built-in alwayson)extensions-builtin/<name>/scripts/(in-repo extensions)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 wrappergitpython_hack.pyexists 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 = adetailerRequires 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*argsinto your hook methods. - Mutate
pbefore sampling — implementprocess(p). - Modify the noise prediction at every step — register a
cfg_denoisercallback (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 toshared_options.py. - Add a custom tab —
on_ui_tabs. - Read PNG metadata into your UI — set
paste_field_names = ["My Setting"]andinfotext_fields = [(component, "My Setting"), ...]on the script.
Entry points for modification
- A change to the
Scriptclass 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_exceptioncalls scattered throughscripts.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.
Previous
SD hijack
Next
Script callbacks