

A BepInEx mod for Hollow Knight: Silksong that lets you change the color of Hornet's cloak.
Works single-player out of the box. If SSMP (Silksong Multiplayer) is also installed, the chosen color is automatically synchronized to every other player on the server so everyone sees your custom cloak in real time.
Source: github.com/Michael4d45/HornetCloakColor
CloakMasks/ and recolors only masked pixels.tk2dSprites whose atlas has a matching mask PNG, including poses outside the player hierarchy.HornetCloakColor.dll and cloak_palette.json into BepInEx/plugins/HornetCloakColor/ (Thunderstore builds include both).BepInEx/plugins/SSMP/ for multiplayer sync.#FFFFFF, not that the cloak is dyed white.Custom and provide a color in the Custom Cloak Color field.
Accepted formats: #AA3344, AA3344, or 170,51,68 (decimal 0–255).Players without this mod will still see vanilla Hornet — they simply won't receive the color updates. You'll still see their vanilla cloaks correctly.
cloak_palette.jsonShipped next to HornetCloakColor.dll. Runtime masking is driven by CloakMasks/; this file only
controls diagnostics and the player hierarchy rescan cadence. You normally do not need to edit it.
{
"debugLogging": false,
"mapIconDebugLogging": false,
"heroMeshRescanIntervalFrames": 30,
"dumpDiscoveredTextures": false
}
debugLogging: when true, logs extra lines (e.g. each cloak color change, how many
scene textures the scanner is matching/ignoring).
Default false.mapIconDebugLogging: logs SSMP map/compass icon synchronization without enabling all cloak logs.heroMeshRescanIntervalFrames: how often the player hierarchy cache is refreshed.dumpDiscoveredTextures: when true, writes the source atlas beside each discovered mask path
as <atlas>-original.png. If a mask is missing, it also writes an empty transparent
<atlas>.png template in the correct CloakMasks/<collection>/ folder so you can paint it.If the file is missing or invalid, the built-in defaults are used.
*-* floating version (whichever is published).# Generate the Silksong path props (once) so the output copies into your install
dotnet new -i Silksong.Templates # only if you don't already have the template
dotnet new silksongpath
# Build
dotnet build HornetCloakColor.csproj -c Release
A thunderstore/dist/*.zip is produced automatically alongside the compiled DLL if thunderstore/thunderstore.toml is configured.
CloakRecolor MonoBehaviour is attached to each player's root GameObject (local hero
and SSMP-spawned remote players). It caches the player hierarchy's MeshRenderers and
refreshes that cache every heroMeshRescanIntervalFrames (default 30) so newly toggled
child objects (some animations use extra meshes or inactive children) are picked up. Per
LateUpdate it just iterates the cache and calls the memoized applier (see below).CloakSceneScanner runs on its own GameObject (DontDestroyOnLoad) at a high
script-execution order. Discovery (FindObjectsByType<tk2dSprite> + filtering) only runs
on SceneManager.activeSceneChanged and on a slow trickle (every ~2 s) for late-spawned
sprites — not per frame. Per LateUpdate the scanner just walks its cached eligible-
renderer list and calls the memoized applier. It covers renderers outside the player
hierarchy (steam-vent recoil, item-get pose, etc.). Renderers under a CloakRecolor and
compass icons are skipped so remote players keep their own colors.CloakMaterialApplier caches per-renderer state (last sharedMaterial
instance ID + applied mode + color). When tk2d swaps a renderer's material mid-animation the
instance ID changes and we redo the work; otherwise the call is a single dict lookup and
early return — SetTexture / SetFloat are not re-pushed every frame. Scene transitions,
palette reloads, and color changes all invalidate the cache so freshly bound materials get
the slow path on first sight.CloakMasks/<collection>/<atlas>.png (R channel). If a sheet still
looks wrong, edit that PNG in the repo and rebuild.HornetCloakColor/CloakHueShift and
pushes the chosen tint in HSV. The fragment shader reads only the R mask texture (no
per-pixel RGB matching at draw time). The shader is shipped as an AssetBundle embedded in the DLL.CloakMasks/Texture2D/<texture-or-sprite>.png. This covers non-tk2d one-off animation
frames such as the diving-bell bench-grab sequence without widening the scanner to every UI
SpriteRenderer.tk2dSprite vertex color and the MeshRenderer material color.IClientAddonNetworkSender / IServerAddonNetworkSender channels exposed by SSMP.playerId -> CloakColor and replays it to any
new joiner so late arrivals see correct colors immediately.CloakMasks/The repo includes CloakMasks/<tk2d collection>/<atlas>.png and a small number of
CloakMasks/Texture2D/<texture-or-sprite>.png standalone SpriteRenderer masks next to the
project root (same paths the mod uses under BepInEx/plugins/HornetCloakColor/). These files
are version-controlled, copied into the build output with the DLL, and included in
Thunderstore zips. Update them in the repo when you tune masks; the game only writes missing
templates when dumpDiscoveredTextures is enabled.
The cloak-only path requires Resources/cloakshader.bundle (embeds CloakHueShift).
It's gitignored, so contributors need to bake it in Unity once. See Shaders/README.md
for step-by-step instructions (TL;DR: open Unity 6000.0.50, drop the shader and editor script in,
HornetCloakColor → Build Shader Bundle, copy the output to Resources/).
If the bundle isn't present the build still succeeds and the mod gracefully falls back to the legacy whole-character tint at runtime.
MIT — see LICENSE.txt.