Architecture
ES modules ("type": "module"), Node 24. All code lives under src/; all prompt content (data)
lives under data/. The one deliberate exception is data/dynamic-prompts/ — the #name
generators are executable .js but are treated as prompt content (authored like lists/expansions),
so they live with the rest of the content under data/. Everything runs with the current working
directory pinned to the project root by
src/chdir.js (which chdirs to its parent), so the many ./output, ./data/lists, ./results.json
style paths resolve from the root regardless of where node was launched.
Top-level layout
src/ ALL code
index.js CLI entry — parses args, runs generation, also hosts the progress server
server.js Web UI entry — Express + Pug app, shells out to the CLI to generate
common.js Shared core — argv, settings accessors, run()/processBatch()/upscale()
chdir.js Side-effect module: process.chdir(parent of src) = repo root. Imported first.
settings.js / image-settings.js / upscale-settings.js / server-settings.js Default settings objects
loadSettings.js merge defaults + user-settings.json (+ legacy migration)
createMissingUserSettings.js / diffSettings.js / convertMetaToJSON.js
applyArgs.js apply presets + command-line overrides
genImg.js call SD WebUI txt2img + progress bars (uses global fetch)
loadVariationData.js / loadRerollData.js / upscaleExisting.js / extendAnimation.js / toAnimation.js
promptFilesAndSuggestions.js scan/classify dynamic prompts, build suggestions
prompt-modules/ The prompt pipeline stages
dynamic-prompt.js expand #name tokens (loads data/dynamic-prompts/* via createRequire)
expansion.js expand <name> tokens list.js expand {name} tokens
prompt-salt.js {salt} handling cleanup.js whitespace/comma cleanup
helpers/ saveImage, saveApng, makeApng, saveResults, listFiles, keywordRepeater,
imageUpscaler, randomEmphasis/Editing/Alternating
core/ the isomorphic engine (engine.js, stages/, node/browserLoader) — see systems/core-engine.md
web/ backend/indexImages.js (image index) + frontend/ (browser JS/CSS) + views/ (Pug)
data/ ALL prompt content
lists/ expansions/ presets/ data files for {name}, <name>, and presets
dynamic-prompts/ ~113 #name plugin modules: export default fn (+ export const full / suggestion_exclude)
v1/ user-submitted/ variant sets
artists.csv / danbooru.csv / nai-tag-expirement.json raw sources
process-*.js one-off scripts that build the lists/ files from the CSV/JSON sources
default-user-settings.json Seed for the user's user-settings.json (root)
output/ generated images + their .json metadata (root, gitignored)
user-settings.json / results.json runtime user data (root, gitignored)
The directory names code consumes are centralized in src/settings.js (listFiles: "./data/lists",
expansionFiles: "./data/expansions", presetFiles: "./data/presets"; dynamicPromptFiles: "dynamic-prompts" now resolves under data/ — the loaders prefix it with ../../data/ (legacy
prompt-modules/dynamic-prompt.js) or data/ (core/nodeLoader.js); promptModuleFiles stays
relative to src/). The web UI's served folder is serverSettings.webFolder = "./src/web".
Data flow — CLI generate
node . ... → common.js loads settings → index.js listens on the progress port, applies
variation/reroll/animation loaders and applyArgs → run() → for each prompt, processBatch() runs
settings.promptModules over the prompt string (expanding #/{}/<>, salt, cleanup) → if images are
enabled and mode is StableDiffusion, src/genImg.js POSTs to the WebUI txt2img API, saves PNG + JSON
via src/helpers/saveImage.js, optionally upscales (src/helpers/imageUpscaler.js) → results written to
results.json and (for animations) an APNG.
Data flow — Web UI
node src/server.js builds the image index (src/web/backend/indexImages.js scans output/*.json), starts
Express on server-settings.port (7861), opens the browser, and serves the Pug pages + a JSON API.
Generation requests (/api/generate, /api/file-variation/:id, /api/upscale-file/:id, …) set args
and spawn the CLI (node . --flags) via child_process; progress is read by polling the CLI's
progress server. The index supports keyword search, a feed, stats, and per-image detail.
The settings accessor pattern
Modules don't import the mutable settings object directly; they call settings() (from
loadSettings.js, re-exported by common.js) which returns the current merged object. This lets the
server reload/replace settings at runtime (/api/reload-settings, /api/replace-settings).
See ../systems/overview.md for the deeper walk-through and
../reference/esm-patterns.md for the module-wiring rules.