CorsacCosmetics is a client-side BepInEx mod for Among Us that adds support for user-provided cosmetic images. This fork updates the original CorsacHats to support newer Among Us versions and expands the available cosmetic types to include hats, visors, and nameplates.
- Hats:
CorsacCosmetics/Hats - Visors:
CorsacCosmetics/Visors - Nameplates:
CorsacCosmetics/Nameplates - Bundles:
CorsacCosmetics/Bundles
Cosmetic bundles (.ccb) allow you to package multiple cosmetics together and set specific cosmetic sprites (eg. preview, climb). Bundles should be placed in the CorsacCosmetics/Bundles folder.
You can create cosmetic bundles with the online editor.
You can use the Crewmate_Base.png file in the Templates folder as a base layer for cosmetics.
Player cosmetics should be 270x428 pixels in size. Nameplates can be any size as they are scaled,
but try to stick to the same aspect ratio as the original nameplate (around 3:1) for best results.
Notes:
- Right now, only PNG format is supported. This will be expanded in future updates.
- Only static images are supported; animated cosmetics are not currently implemented.
- There aren't any templates besides the examples yet.
- The loaders currently look for PNG files only ("*.png").
- Use a unique filename for each cosmetic, for example: my_cool_hat.png
- Each cosmetic type has an associated metadata you can set by making a JSON file with the same name as the PNG file. For example, for
my_cool_hat.png, create amy_cool_hat.jsonfile in the same folder with the following structure:
{
"Name": "MyHat",
"MatchPlayerColor": true,
"BlocksVisors": false,
"InFront": true,
"NoBounce": false
}CorsacCosmetics provides a PluginCompat static class that allows other mods to add cosmetics programmatically. This is designed to work with soft-dependencies, so your mod doesn't need to reference CorsacCosmetics directly.
If you are downloading resources at runtime, you should do it before Corsac discovers and installs cosmetics. Use QueueDiscoveryTask to register an async task that Corsac will wait for before proceeding:
async Task DownloadCosmetics()
{
using var httpClient = new HttpClient();
var bytes = await httpClient.GetByteArrayAsync("https://example.com/my_cosmetics.ccb");
var outputPath = Path.Combine(CosmeticPaths.BundlePath, "my_cosmetics.ccb");
await File.WriteAllBytesAsync(outputPath, bytes);
}
// Queue the task before Corsac runs discovery (for example, in Plugin Load)
PluginCompat.QueueDiscoveryTask(DownloadCosmetics());This ensures your resources are fully downloaded and saved before Corsac attempts to load cosmetics. Multiple tasks can be queued and they will be executed in order.
For convenience, you can use QueueBundleDownload to download a .ccb bundle from a URL. The bundle will be automatically saved to the CorsacCosmetics/Bundles folder with the filename from the URL:
// Download to default Bundles folder
PluginCompat.QueueBundleDownload("https://example.com/my_bundle.ccb");
// Or specify a custom output folder
PluginCompat.QueueBundleDownload("https://example.com/my_bundle.ccb", @"path\to\custom\folder");If you already have bundle data in memory (e.g., from an embedded resource or network stream), you can use AddBundleBytes to register it directly:
byte[] bundleData = GetBundleBytesFromSomewhere();
PluginCompat.AddBundleBytes(bundleData);You can register a folder for CorsacCosmetics to load cosmetics from. The directory structure must match Corsac's default structure (with Hats, Visors, Nameplates subfolders). The Bundles subfolder is not included in the folder source:
PluginCompat.AddFolderSource(@"path\to\your\cosmetics\folder", "your_group_name");The groupName parameter is optional and defaults to "default". This can be used to organize cosmetics from different sources.
You can also load cosmetics from a .ccb bundle file:
PluginCompat.AddBundleSource(@"path\to\your\bundle.ccb");For deeper integration, you can implement the ICosmeticSource and ICosmeticAssetReader interfaces to create fully custom cosmetic loading pipelines.
Implement this interface to create a custom source for discovering cosmetics:
public interface ICosmeticSource
{
string SourceId { get; }
Task<IEnumerable<CosmeticDescriptor>> DiscoverAsync();
}SourceId: A unique identifier for your sourceDiscoverAsync(): Returns a collection ofCosmeticDescriptorobjects representing the cosmetics your source provides
When creating CosmeticDescriptor, try to pass a unique value into the sourceId parameter to ensure proper namespacing.
For example, the built-in LocalBundleSource passes a hash of the bundle manifest, guaranteeing that different bundles will
have different group IDs and cosmetic IDs.
Implement this interface to handle loading sprite assets for your cosmetics:
public interface ICosmeticAssetReader
{
Task<Sprite?> LoadSpriteAsync(string spriteKey);
}LoadSpriteAsync(string spriteKey): Loads and returns aSpritefor the given key. The spriteKey can be used to request different sprite variants (e.g., "preview", "climb").
It is highly recommended not to keep a cache of sprites for your ICosmeticAssetReader. The purpose of this system is to create
a sprite when it is needed, and to unload the sprite when it is no longer being used. If you keep a sprite cache, you are
defeating the purpose of the system.
When implementing ICosmeticSource.DiscoverAsync(), you'll need to create CosmeticDescriptor instances:
var descriptor = new CosmeticDescriptor(
sourceId: "MyCustomSource",
group: "my_group",
name: "My Custom Hat",
type: CosmeticType.Hat,
metadata: new HatMetadata { Name = "MyHat", MatchPlayerColor = true },
reader: new MyCustomAssetReader()
);The CosmeticDescriptor class has the following properties:
Id: Auto-generated unique identifierDisplayName: The display name of the cosmeticGroup: The group this cosmetic belongs toType: The cosmetic type (Hat,Visor, orNamePlate)SourceId: The source that provided this cosmeticMetadata: The cosmetic metadata (implementsICosmeticMetadata)AssetReader: The reader used to load sprites (implementsICosmeticAssetReader)
Register your custom source with the SourceRegistry:
var mySource = new MyCustomSource();
SourceRegistry.Instance.RegisterSource(mySource);- Any custom cosmetic will be visible to you and to other players who have the same mod and the same cosmetic files installed.
- Players who do not have the mod (or have different files) will likely see nothing.
- Make sure you have BepInEx and Reactor installed for Among Us. This plugin depends on Reactor.
- Copy the mod DLL (CorsacCosmetics.dll) into
BepInEx\plugins. - Start Among Us. The mod will create the
CorsacCosmeticsfolder and subfolders forBundles, Hats,Visors, andNameplates` if they don't already exist. - Place your
.ccbbundles and custom PNG files into the appropriate subfolder (Bundles, Hats, Visors, Nameplates). - (Optional) Create corresponding JSON metadata files for each PNG cosmetic as described above.
- Restart Among Us to load the new cosmetics.
- Enjoy your custom cosmetics in-game!
- Updated to support more recent Among Us versions.
- Added Visors and Nameplates as cosmetic types.
- Added custom bundle format (
.ccb)
This mod is not affiliated with Among Us or Innersloth LLC, and the content contained therein is not endorsed or otherwise sponsored by Innersloth LLC. Portions of the materials contained herein are property of Innersloth LLC. © Innersloth LLC.
- This mod is not affiliated with Among Us or Innersloth LLC.
- Original CorsacHats project and contributors for inspiration.
- See the LICENSE file in this repository for license information.