Skip to content

Mod authoring

Every mod is a directory next to its manifest:

my-chat-theme/
mod.yaml
assets/
icons/
chat_icon.png
config.json # created/updated by Carbon when settings change
  • mod.yaml: required manifest (apiVersion: carbon.mods/v1).
  • assets/: files referenced by source paths in operations.
  • config.json: leave this to Carbon unless you are debugging; the app writes user settings here.

Install community mods from the Carbon desktop app (Mods → Install Folder or Install Zip). They are stored under <Carbon root>/mods/<id>/. Built-in mods live in the repo at project/builtin-mods/<id>/ and ship inside the app binary; your toggles and config.json for built-ins go to bin/configs/mods/<id>/.

apiVersion: carbon.mods/v1
id: my-silent-oof
name: Silent Oof
author: your-name
version: 1.0.0
description: Replaces the death sound with silence.
tags:
- sounds
priority: 5
operations:
- type: replace-file
target: content/sounds/oof.ogg
source: assets/oof.ogg

Drop a silent oof.ogg at assets/oof.ogg, enable the mod, and launch.

Use settings for UI controls and reference keys in operations:

apiVersion: carbon.mods/v1
id: my-spinner
name: Spinner Color
author: your-name
version: 1.0.0
description: Recolor the loading spinner.
tags: [ui, loading]
priority: 6
settings:
- key: color
type: dropdown
label: Spinner color
default: blue
options: [blue, pink, white]
operations:
- type: replace-file
target: ExtraContent/textures/ui/loading/circle_dark.png
source: assets/{color}/circle_dark.png
- type: replace-file
target: ExtraContent/textures/ui/loading/circle_light.png
source: assets/{color}/circle_light.png

when and unless take a setting key (booleans for toggles):

settings:
- key: useCustom
type: toggle
label: Use custom cursor
default: false
operations:
- preset: cursor
source: assets/cursors/{cursor}.png
unless: useCustom
- preset: cursor
source: "{customCursor}"
when: useCustom

When you replace many related files, use a preset operation instead of listing each Roblox path. Carbon expands presets at launch (see project/carbon/Mods/ModPresetRegistry.cs).

Start with the Preset operations guide, it documents:

  • The full preset catalog (cursor, menu-icons, sounds, skybox, fonts, …)
  • Required folder layouts (for example assets/sky/{name}/cubemap + dynamic)
  • How Menu Bar Pack settings in the app map to config.json and a menu-icons merge

Quick copies:

# Top menu bar: match filenames in project/builtin-mods/menubar-pack/assets/menu/
operations:
- preset: menu-icons
source: assets/menu/{theme}
mode: merge
# Sky: needs cubemap/ and dynamic/ subfolders (see skybox-pack)
operations:
- preset: skybox
source: assets/sky/{preset}
mode: merge
# Sounds: filenames must match Roblox (action_jump.mp3, etc.)
operations:
- preset: sounds
source: assets/sounds
mode: merge

The built-in mods catalog links each shipped mod to these patterns.

  1. id must be unique, lowercase, and hyphenated. Carbon uses it as the stable mod identifier.
  2. priority: bump it when your mod needs to win over another mod on the same target.
  3. Match Roblox paths exactly. A wrong target often fails quietly. Compare with built-in mods that touch the same UI.
  4. Ship @2x / @3x variants when you replace UI PNGs that Roblox ships at multiple resolutions.
  5. Test in small steps: one replace-file, confirm in-game, then grow the mod.

Zip the mod folder (mod.yaml + assets/). You can omit config.json unless you want default settings baked in. Share on Discord or GitHub; recipients install through the desktop app.

For every field, see the mod.yaml reference.