Skip to content
⌂ Home

Workshop SDK Tutorial

For authors who publish Steam Workshop themes, lyrics scenes, visualizers, EQ presets, locale packs, or sandbox plug-ins.

SDK repository: github.com/Moekotori/echo-workshop-sdk

Workshop SDKLocal plug-ins
DistributionSteam Workshopplugins/ folder
RuntimeECHO sandboxPlug-ins page in ECHO
TargetSteam ECHOECHO Next local plug-ins
Toolingecho-workshop-sdk CLIHand-written echo.plugin.json

After users subscribe, they must click Use inside ECHO — see Steam Workshop. These SDK commands never upload to Steam. Publishing happens only in ECHO → Workshop → Authoring Studio.

GuideBest for
Themes: colors to CSSFirst skin or packaged stylesheet
Sandbox plug-ins: commands and catalogsHello plug-in, network catalog, permissions
JSON content: lyrics / visualizer / DSP / localeNon-code Workshop items
Runtime UI themesSandboxed HTML/CSS/JS replacement UI
Official examples and snippetshello / cinema / fragments / snippets
Local dev workflowdev / watch / quality / next / --json
Lyrics scene walkthroughCinema slots, transport, mini player
Locale packslocale.json, fallback, wenyan example
Visualizer and DSPbars/wave/radial + 31-band EQ
VST3 profileClassId mapping, no binaries
Catalog APIsearch / browse / resolve
Publish and updateShip after a clean local check
Author FAQBoundaries, choices, versions, AI help
  • SubscribersSteam Workshop — no SDK required.
  • Authors → this series; after check, follow publish.

A passing local check does not replace Steam download, Use, or production host policy. Test subscribe flow with a real Steam account before and after publish.

A workspace from init or example looks like this:

my-project/
├── echo.workshop.project.json # Authoring project: tags, description, visibility, preview path
├── preview.png # 256×256 listing image (replace placeholder before publish)
├── package.json # npm run check / dev / watch
├── content/ # Shipped Workshop payload
│ ├── echo.workshop.json # Outer manifest: id, version, file hashes, compatibility
│ └── theme.json etc. # Depends on kind (see table)
├── src/ # Plug-in sources (synced into content/community.echo on check)
│ └── plugin.js
├── .echo-sdk/ # Portable SDK copy + TypeScript declarations
└── .vscode/ # JSON Schema, tasks, echo- snippets
KindYou editcontent/ entry
Themecontent/theme.json (+ optional theme.css / ui/)theme.json
Lyrics scenecontent/lyrics-style.jsonlyrics-style.json
Visualizercontent/visualizer.jsonvisualizer.json
DSPcontent/dsp.jsondsp.json
Locale packcontent/locale.jsonlocale.json
Sandbox plug-insrc/plugin.js (+ panels)community.echo (generated)

After changing content/ or src/, run npm run check. The SDK syncs and recomputes files[].sha256 in echo.workshop.json.

ECHO does not ship Netease, Spotify, YouTube, or other streaming platforms. Plug-ins may expose an author-owned HTTP(S) direct-link catalog; playback starts only after the user confirms. Legal boundary → Download and plug-in source policy.

ItemRequirement
Node.js20+ (init / check / dev need only Node, not ECHO)
EditorVS Code recommended; generated projects include JSON Schema and snippets
ECHOSteam build for publishing via Authoring Studio
SteamLaunch ECHO from Steam while online

All three paths ship the same package:

  1. Clone from GitHub
    Terminal window
    git clone https://github.com/Moekotori/echo-workshop-sdk.git
    cd echo-workshop-sdk
  2. Install via npm
    Terminal window
    npm install github:Moekotori/echo-workshop-sdk
    npx echo-workshop-sdk version
  3. Steam starter item — subscribe to Workshop item 3784997717.

Current package version 1.11.0, manifest schema 1, sandbox plug-in API 2:

Terminal window
node .\bin\echo-workshop-sdk.mjs version --json

Walk through the local gate with the hello plug-in example.

  1. After cloning the SDK, copy the example into an empty folder:
    Terminal window
    node .\bin\echo-workshop-sdk.mjs example hello-plugin .\my-hello
    cd .\my-hello
  2. See what to do next:
    Terminal window
    npm run next
  3. Run the full local gate (sync, validate, quality, fixtures):
    Terminal window
    npm run check
  4. Open the author console:
    Terminal window
    npm run dev

hello-plugin ships one command and a single playback:read permission. Edit src/plugin.js; check syncs into content/community.echo.

Prefer a theme first:

Terminal window
node .\bin\echo-workshop-sdk.mjs example minimal-theme .\my-theme
cd .\my-theme
npm run check
--kindPurposeTypical start
themeSkins, packaged CSS, custom UI--recipe css-theme or --preset skin
lyrics-styleLyrics scene layout--recipe cinema-lyrics
visualizer-presetSpectrum style (bars / wave / radial)--recipe radial-visualizer
dsp-preset31-band EQ preset--recipe vocal-eq
audio-plugin-profileLocal VST adapter notes--kind audio-plugin-profile
locale-packLanguage ECHO does not ship--recipe wenyan-locale
plugin-packageSandbox plug-in--recipe plugin-complete or hello-plugin

Pick by outcome instead of memorizing kinds:

Terminal window
node .\bin\echo-workshop-sdk.mjs recipes
node .\bin\echo-workshop-sdk.mjs init .\harbor --recipe css-theme
--recipeKindYou get
colors-themeThemeLight/dark tones only
skin-themeThemeDeclarative chrome and atmosphere
css-themeThemePackaged CSS (stylesheet tier)
custom-uiThemeSandboxed HTML/CSS/JS UI
editorial-lyricsLyricsCover + lyrics grid (default stage)
cinema-lyricsLyricsCinema stage; often hides mini player
compact-lyricsLyricsCompact one-line header
cover-lyricsLyricsLarge cover column
bars-visualizerVisualizerMirrored bars
wave-visualizerVisualizerWaveform
radial-visualizerVisualizerRadial spectrum
flat / vocal-eq / bass-eqDSPFlat / vocal / bass 31-band presets
plugin-completePlug-inCommands, panel, agent, providers
source-catalogPlug-inAuthor-owned direct-link catalog
lyrics-sourcePlug-inLyrics provider + current-lyrics panel
wenyan-localeLocaleLiterary Chinese and similar
  1. Initializeinit or example; folder name becomes id/title.
  2. Edit — JSON under content/ for themes/lyrics/visualizer/DSP; src/plugin.js for plug-ins.
  3. Iteratenpm run next for fix-first items; add / set / scaffold for incremental changes.
  4. Verifynpm run check must pass before publish; use npm run check -- --warn-only while iterating.
  5. Previewnpm run dev opens the author console; several kinds also get fixture preview.
  6. Publish — validate and upload in ECHO → Workshop → Authoring Studio. SDK commands never upload.

Print the full checklist:

Terminal window
node .\bin\echo-workshop-sdk.mjs guide checklist
CommandPurpose
init ./dir --kind themeNew project
init ./dir --recipe cinema-lyricsNew project from a recipe
example hello-plugin ./dirCopy an official example
next .Fix-first items, then allowed slots/permissions
add . --permission library:readAppend a whitelisted permission
set . --title "Harbor Night"Update common fields
scaffold . --preset runtimeStack theme tiers without restarting
check .Complete local gate
dev .Author console (port 41783, auto-fallback)
watch .Rerun check on save
fix .Repair preview.png, README, minEchoVersion
guide / guide troubleshootChinese cookbook and troubleshooting table
snippet listCopy-paste starters with required permissions
api echo.queue.moveItemMethod-to-permission lookup
api errorsWhich errors may be retried

Generated projects expose npm run check and npm run dev. VS Code default build task: ECHO Workshop: Check (Ctrl+Shift+B).

Generated projects include:

  • JSON Schema — invalid fields in echo.workshop.json, theme.json, etc. are underlined while editing
  • Snippets — type an echo- prefix in .js / .json (same set as snippet list)
  • Plug-in types.echo-sdk/echo-workshop-plugin.d.ts completes the echo.* global

The bundled .github/workflows/validate-workshop.yml runs the same check gate on every push and pull request and mirrors the summary into the GitHub Actions job summary. The template uses Node 22; local Node 20+ is fine.

  1. After pushing to GitHub, read the gate summary on the PR — PASS means the local gate passed in CI.
  2. If CI fails with Generated files are committed: run npm run check && git add -A locally and commit synced manifests.
  3. Publishing still happens only in ECHO Authoring Studio; CI never uploads to Steam.

Upgrade without starting over:

PresetWhat you getMin ECHO
colorsLight/dark tones only26.8.15
skinDeclarative chrome (default)26.8.15
stylesheetPackaged CSS26.8.20
runtimeSandboxed HTML/CSS/JS UI26.8.20

Example:

Terminal window
node .\bin\echo-workshop-sdk.mjs scaffold .\my-theme --preset stylesheet
  • basePreset must be a public host preset such as classic. FINAL, nyanCat, and darkSideMoon are rejected.
  • Packaged CSS must live under html[data-workshop-theme-pack="<your-pack-id>"].
  • Inline scripts, remote @import, and non-raster url() values are blocked.
  • Declare minEchoVersion ≥ 26.8.20 for stylesheet and runtime themes.

Workshop plug-ins share ideas with local plug-in authoring but run in a narrower sandbox.

PresetBest for
basicOne command, minimal permissions
completeCommands, panel, agent, providers
catalogAuthor-owned direct HTTP(S) catalog — not official streaming
lyricsLyrics provider plus current-lyrics panel

Projects reference .echo-sdk/echo-workshop-plugin.d.ts for API 2 completion on the echo global.

At most 32 files, 512 KiB per UTF-8 file, 2 MiB serialized package. Allowed extensions: .css, .html, .js, .mjs, .json. Inner plug-in apiVersion must exactly match outer compatibility.pluginApiVersion.

Network plug-ins must declare networkHosts (bare domain or public IPv4, no protocol/port) and request network:request. The mock host rejects undeclared destinations locally:

Terminal window
node .\bin\echo-workshop-sdk.mjs add . --permission network:request
node .\bin\echo-workshop-sdk.mjs api errors

Lyrics scenes — the host owns all slots; you place whitelisted styles. Slots include cover, title, lyrics, current-line, spectrum, play-toggle, and more. If you hide the mini transport bar, you must ship play-toggle.

Visualizer — host styles are bars, wave, and radial only — no particles. Palette: 1–8 unique #rrggbb colors; barCount 8–128.

DSP / EQ — official 31-band layout; gain -12..12, Q 0.1..12, preamp -12..6. JSON is a preset; Audio Core executes at playback time.

  1. Clean npm run check (decide whether warnings are acceptable).
  2. Replace placeholder preview.png (256×256); write README and listing copy (≥ 80 characters recommended).
  3. Align license, tags, and minEchoVersion with what you actually tested.
  4. Launch ECHO from Steam → Workshop → Authoring.
  5. Import or link the local project and run ECHO validation.
  6. Publish after validation passes. Keep public starter ID 3784997717 — do not create a new one when updating.

User-side subscribe/use flow → Steam Workshop.

Run npm run check and read the one-line gate summary at the end.

SymptomLikely causeFix
Manifest hash mismatchEdited content/ or src/ without syncRerun check (sync runs automatically)
capability-deniedUndeclared permissionadd . --permission <permission>
network-host-deniedRequest to undeclared hostDeclare networkHosts in outer manifest
Preview must be …Invalid preview imagenpm run fix
Port 41783 was busyDev port takenOmit --port to auto-pick the next free port

See TROUBLESHOOTING.md and CHEATSHEET.md in the SDK repo.

Terminal window
node .\bin\echo-workshop-sdk.mjs guide troubleshoot

SDK types, schemas, CLI, templates, and examples are MIT licensed. Your original Workshop content stays yours; declare a license with --license on init / set (default All-Rights-Reserved).

To change the SDK itself, open issues or PRs at echo-workshop-sdk — see CONTRIBUTING.md.