Skip to content

Shader mod#

Ship visual changes as HLSL files without hooking game code. Two layouts:

Per-mod shaders (mods/<id>/)#

Use this when the shader belongs to one mod package.

mods/red-tint/
├── mod.json
├── main.luau
└── assets/
    └── red_tint.hlsl

mod.json:

{
  "id": "red-tint",
  "name": "Red Tint",
  "type": "luau",
  "entry": "main.luau",
  "version": "1.0.0",
  "hostVersion": "0.8.0",
  "engineVersion": "1.0.0"
}

Every luau mod must return a class from mod(). tick and shutdown default to empty on Petrichor.Mod. Override them only if you need them.

local Shaders = require("Courage.Shaders")
local Assets = require("Courage.Assets")

local RedTint = mod("red-tint")

function RedTint:init()
    Shaders.set_enabled(Assets("red_tint.hlsl"), true)
    Shaders.apply()
end

return RedTint
import * as Shaders from "Courage.Shaders"
import * as Assets from "Courage.Assets"

declare function mod(name: string): any

const RedTint = mod("red-tint")

RedTint.init = function () {
    Shaders.set_enabled(Assets("red_tint.hlsl"), true)
    Shaders.apply()
}

export = RedTint

Put HLSL in assets/red_tint.hlsl. See the shader guides for uniforms and examples.

Global shaders (mods/shaders/)#

Drop .hlsl files under mods/shaders/ with no mod.json. They register under short names (e.g. global_fx).

Good for quick tests that you toggle from the dev Shader Mod Loader.

File Registered name
mods/shaders/global_fx.hlsl global_fx

Toggling at runtime#

  • Dev menu: Shader Mod Loader (hooks-enabled builds).
  • Luau: Courage.Shaders.set_enabled and apply() from init or tick. See Courage.Shaders.

Next steps#