Directory Structure
This page explains how to organize files in a Desktop Homunculus MOD. A MOD can be as simple as three files -- the only hard requirement is a package.json with the homunculus field. Everything else depends on what your MOD does.
Minimal MOD
A character MOD that spawns a VRM model needs just a package config, a service script, and the model file:
my-character/
├── package.json # Package config with homunculus field
├── index.ts # Service (runs on app launch)
└── assets/
└── MyModel.vrm # VRM character model
package.json -- Must include "type": "module" and the "homunculus" field declaring your service script and assets. See Package Configuration for details on every field.
index.ts -- The service script specified by "homunculus.service". The engine runs it automatically via tsx, so you can write TypeScript directly without a build step. This script typically spawns your character and sets up behaviors.
assets/ -- Convention for storing binary assets (VRM models, animations, sounds, images). The directory name is flexible -- the actual file paths are declared in package.json -- but assets/ is the standard across official MODs.
This is all you need for a working MOD. Install it with hmcs mod install <path-to-mod> and your character appears on the desktop.
MOD with UI
A MOD that provides a settings panel or other UI adds a React app and on-demand bin commands:
my-settings-mod/
├── package.json # Declares assets, menus, and bin commands
├── commands/
│ └── open-ui.ts # Bin command script (invoked via HTTP API)
└── ui/
├── index.html # Vite entry point
├── vite.config.ts # Vite build config
├── tsconfig.json # TypeScript config for the UI app
└── src/
├── main.tsx # React entry point
├── App.tsx # Root component
├── index.css # Styles (Tailwind)
└── components/ # UI components
commands/
On-demand scripts exposed via the "bin" field in package.json. These run only when explicitly called through the HTTP API (POST /commands/execute). The conventional directory name is commands/ or bin/.
ui/
A React + Vite app that renders inside a WebView. The source lives in ui/src/ and builds to ui/dist/. The built ui/dist/index.html is declared as an html asset in package.json -- the engine loads it when opening the WebView.
UI apps typically import @hmcs/ui for shared components and use Tailwind for styling. This is the only part of a MOD that requires a build step (pnpm build from the ui/ directory).
Note that ui/dist/ is a build artifact and is not shown in the tree above. It is generated by running pnpm build in the ui/ directory.
This example has no index.ts at the root -- it uses only on-demand commands and a UI panel. A MOD can also combine both patterns: a service, a UI app, and bin commands in the same package.
File Reference
| File / Directory | Purpose | Required |
|---|---|---|
package.json | Package config with homunculus field | Yes |
index.ts | Service (runs on app launch) | No |
assets/ | Binary assets (VRM, VRMA, sounds, images) | No |
commands/ or bin/ | On-demand bin command scripts | No |
ui/ | WebView UI app (React + Vite) | No |
ui/dist/ | Built UI output (declared as html asset) | Only if ui/ exists |
lib/ | Shared utility code used by scripts | No |
Conventions
- Store binary assets in
assets/. The directory name is a convention, not a requirement -- actual file paths are declared inpackage.json. - Place bin command scripts in
commands/orbin/. Both are common across official MODs. - UI apps live in
ui/with build output inui/dist/. - Shared helper code goes in
lib/. - The only required file is
package.json. Everything else depends on what your MOD does.
Next Steps
- Package Configuration -- details on every
package.jsonfield - Asset IDs -- how asset identifiers work