01 Overview
Catalog is a Roblox experience whose core is an in-game avatar editor: the player browses Roblox's official catalog, tries on clothing and accessories over a live 3D preview, adjusts body colors and proportions, saves outfits, shares them with the community via codes, and buys items directly.
The project is split into two halves that talk to each other through a typed Remotes layer:
- Client — all UI, the 3D preview, reactivity, and presentation logic. Replicated to players.
- Server — the authority: DataStore persistence, validation, catalog queries, purchases, moderation, and everything that must not be trusted to the client.
Main features it delivers
- Paginated catalog browsing and search, with categories, subcategories, and filters.
- 3D preview with an orbit camera, clothing try-on, body color editing, and proportion scaling.
- Accessory refinement (move, rotate, scale).
- Undo / redo of avatar changes.
- Player inventory and favorites.
- Saving personal outfits and publishing outfits to the community via shareable codes.
- Community outfit browser with sorting, gender filters, and creator filters.
- Emotes played on the real character.
- Single and bulk purchases, plus Robux gifting between players.
- A style-preference onboarding that feeds physical outfit stands in the world.
- Chat tags by group role, a player-to-player likes system, leaderboards.
- An admin command system (kick, ban, outfit deletion) based on group rank.
- A loading / intro screen shown before the main UI.
02 Tech stack & tooling
| Item | Use in the project |
|---|---|
| Luau | The single language for all code (client and server). Extension .luau; only entry points use the init.* names Rojo requires. |
| Rojo | Syncs the repository code into Roblox Studio. The folder→location mapping is in the runtime section. |
| Charm | Reactive state library (signals and derived values). The foundation of UI reactivity. |
| CharmSync | Replicates reactive server state to the client over a Remote. |
| ProfileStore | Third-party DataStore wrapper for persistent player data with safe sessions. |
| Spr | Spring-based animation library for smooth UI transitions. |
| Trove / Signal / Observers | Lifecycle utilities: resource cleanup, custom events, and instance observation. |
| TypedRemote | Wrapper that creates and types the RemoteEvent/RemoteFunction objects centrally. |
Third-party libraries are vendored (copied into the repo) under their original names, kept separate from in-house code.
03 Roblox services used
The following Roblox engine services are used across the codebase. The "scope" column notes whether the usage is mainly client, server, or both.
| Service | Scope | What it is used for |
|---|---|---|
ReplicatedStorage | Both | Root container for the shared AvatarCreator tree and the runtime Remotes folder; every module resolves dependencies from here. |
Players | Both | Local player access, player added/removing lifecycle, friend checks, rank-in-group queries, and the official ban/unban API. |
MarketplaceService | Both | Prompting purchases, processing developer-product receipts, querying product info, and prompting subscription/Robux flows. |
AvatarEditorService | Server | Searching the official catalog, resolving item/bundle descriptions, and inferring bundles by name. |
DataStoreService | Server | Backing store for persistent data (player profiles via ProfileStore, the community outfit index, and the deletion blocklist). |
RunService | Both | Frame and physics step hooks for the preview camera, animations, and Studio-vs-runtime checks. |
TweenService | Client | UI tweening alongside the spring library for transitions and effects. |
UserInputService | Client | Mouse, touch, and keyboard input for preview rotation/pan/zoom and shortcuts. |
ContextActionService | Client | Bound action shortcuts (e.g. free camera). |
CollectionService | Client | Tag-based UI effects (hover/click effects, animated textures) applied across many elements. |
HttpService | Server | JSON encode/decode and outbound requests for the asset→bundle reverse lookup via a proxy host. |
TextChatService | Client | Injecting chat tags and gradients into chat messages. |
Chat | Both | Legacy chat surface support where needed. |
SocialService | Client | Friend-related social surfaces (e.g. gifting to friends). |
TeleportService | Server | Teleport flow used by an admin command. |
MessagingService | Server | Cross-server messaging where needed. |
GroupService | Server | Resolving creator-group information. |
InsertService | Server | Loading assets on demand. |
SoundService | Client | Sound playback configuration (respects filtering). |
Lighting | Both | Scene/lighting setup for environments and preview. |
GuiService | Client | GUI insets and screen metrics. |
StarterGui | Client | Toggling default Roblox UI elements. |
ContentProvider | Client | Preloading assets during the intro. |
PhysicsService | Server | Collision-group setup (e.g. disabling player-player collisions). |
ServerScriptService | Server | Root container for the AvatarCreatorServer tree. |
workspace | Both | World objects: outfit stands, leaderboards, trampolines, and the baseplate. |
Engine objects also used through Roblox APIs include HumanoidDescription (full avatar appearance),
Animation/AnimationTrack (emotes and leaderboard models), and Camera
(scriptable preview camera).
04 Runtime architecture — the 4 roots
At runtime the project lives under four roots inside Roblox's DataModel. These names are stable: they never change even when the interior is reorganized.
| Repository folder | Roblox location | Nature |
|---|---|---|
src/avatarCreator | ReplicatedStorage.AvatarCreator | Client code + shared modules. Replicated to all players. |
src/avatarCreatorServer | ServerScriptService.AvatarCreatorServer | Server code. Never replicated to the client. |
src/client | StarterPlayer.StarterPlayerScripts.Client | Player LocalScript entry points. |
src/replicatedFirst | ReplicatedFirst.IntroClient | Intro / loading screen, loaded before everything else. |
Additionally, the server creates a Remotes folder (AvatarCreatorEvents) in ReplicatedStorage at
runtime, where the RemoteEvent and RemoteFunction objects both halves use live.
AvatarCreator and all server
code off AvatarCreatorServer. There are no loose "Shared" or "Server" folders outside these roots.
05 Hybrid architecture — Core + Features
Each root's interior follows a hybrid layout: a cross-cutting base (Core, Config, Domain) plus vertical per-feature folders (Features). The rule is simple: cross-cutting, reusable things go in the base; things that belong to one feature go in that feature's folder.
Cross-cutting layers (client)
| Layer | What it holds |
|---|---|
Core/Packages | Vendored third-party libraries (reactive state, springs, signals, cleanup, sync, etc.). |
Core/Lib | In-house generic utilities: number formatting, table tools, logger, rich-text escaping, UI helpers, item-info cache. |
Core/Net | Remote definitions, serialization, and shared cache-key generation. |
Core/Types | Shared types, interfaces, and "tuning" constants. |
Config | Static configuration data: limits, images, purchase tiers, themes, group roles, chat tags, etc. |
Domain | Pure business logic with no UI or state: item adaptation, detail lookup, product parsing, chat name color. |
Features (client)
Each feature groups its controllers, panels, views, and state in its own folder:
The same principle applies on the server, with Core, Data, Admin,
Runtime, and a set of Features that mirror the client's.
06 Security model — client vs server
In Roblox, anything living in ReplicatedStorage is replicated to players and can be inspected. The guiding principle is therefore:
The client is never the source of truth. The server validates and decides everything that matters.
- Server authority: purchases, data writes, admin permissions, accessory/makeup limits, and applying the avatar description are resolved and validated on the server.
- Sensitive data off the client: the full catalog schema, server-only identifiers, and server-side serialization live in the server root, where they are not replicated.
- The client sends intent, not decisions: e.g. "I want to buy this item" or "I want to equip this"; the server checks and executes.
As part of hardening, modules only the server needs (catalog schema, leaderboard animation identifiers, and description serialization) live in the server root instead of ReplicatedStorage, reducing the exposed code surface without affecting behavior: the client still receives categories through a RemoteFunction rather than reading the schema directly.
07 Client structure
Entry points (src/client)
These are LocalScripts. The main one acts as a pure orchestrator: it holds no business logic. Its job is to require modules, resolve all UI references once, inject explicit dependencies into each module, and start the boot sequence. There are also auxiliary entries for the donation board and the world trampolines.
The boot sequence, at a high level:
- Parallel boot of chat and purchases.
- Initialization of stateful services (favorites, catalog, inventory, outfits).
- Single resolution of every UI reference into one table.
- Dependency injection into each module (order matters).
- Wiring of emote playback and the HUD buttons.
- Start: categories, tabs, and the catalog grid.
Client module families
| Family | Responsibility |
|---|---|
Controllers (Controller suffix) | Orchestrate open/close and wiring of a feature (catalog, emotes, outfits, community, gifting, settings, onboarding, HUD). |
Panels and menus (Panel / Menu) | The concrete UI of each feature. |
Cards and views (Card / View) | Presentation of a single item or outfit. |
Stores (Store) | Shared reactive state built on signals. |
| UIKit | Reusable components: widgets (switches, sliders, color pickers, dropdowns), visual effects, the 3D preview, the panel manager, and the responsive grid. |
Canonical systems (single source of truth)
To avoid duplication, certain responsibilities have a single owner:
- Favorites — one service holds the favorites set; everything else consumes it.
- UI references — one module resolves all UI-tree references; reference-lookup chains are never duplicated at boot.
- Avatar state — one central module holds the client avatar state (critical; it follows a strict order: state is declared before functions).
- Emote state — one controller centralizes which emote is currently playing.
- HUD buttons — one controller wires all HUD buttons; it is the most connected node on the client (acting as a bridge across many features).
- Item-info cache — one module caches product info and resolves lookups in parallel, avoiding serial calls.
08 Server structure
The server has its own thin orchestrator that initializes services and registers each feature's handlers.
| Folder | Contents |
|---|---|
Data | Persistence wrapper (ProfileStore), the public player-data API, and a "guard" that validates and cleans the avatar description before persisting it. |
Core | Server utilities (e.g. safe description application, formatting, fetching player data). |
Config | Static data only the server needs (catalog schema, leaderboard animation identifiers). |
Admin | Modular command system: command router, group-rank permission manager, ban service, and a per-category commands folder. |
Runtime | Scripts that auto-run at server start (player collisions, leaderboard animations, leaderboards, etc.). |
Features | One folder per feature, each with its services and a handlers subfolder. |
Handler pattern
Handlers take two shapes depending on their nature:
- Query (search, pagination, groups, categories): expose a function bound to a RemoteFunction.
- Effect / subscription (avatar, outfits, codes, bundles, favorites, emotes, settings, style preference): initialize with side effects and create their own Remotes.
Common dependencies (schema, cache keys, catalog services, player data, constants) are injected into handlers rather than each one resolving them on its own.
Catalog services (server)
They live within the catalog feature: a layer that queries Roblox's official catalog, a per-player paginated cache, active pagination sessions, and creator-group resolution.
09 Configurable files (Config)
The files under ReplicatedStorage.AvatarCreator/Config are the project's tuning knobs:
static data meant to be edited to customize the game without touching logic. The most important ones:
| File | What it configures | Edit when you want to… |
|---|---|---|
AvatarConstants | Central constants: asset-type display names, default clothing references, avatar-description field maps (numeric, string, color, scale), core cached bundles, default mood, and the group id used across the game. | Change defaults, the target group, or how avatar fields are mapped. Most depended-on Config file — handle with care. |
AccessoryLimits | Maximum rigid and layered accessories the player may wear (counting logic included). | Adjust how many accessories of each kind are allowed. |
MakeupLimits | Maximum number of makeup layers. | Allow more or fewer makeup layers. |
PurchaseTiers | Cosmetic purchase tiers selected by price: celebration color, confetti amount, and effect duration per price bracket. | Re-tune the post-purchase celebration by price. |
FeaturedItems | Manually curated list of catalog item ids shown first in the "Featured" tab; list order = display order. | Change which items are featured. Edit only this list. |
Images | Central registry of UI image asset references (label icons, item banners, gender icons, arrows, and more). | Swap any UI icon/image in one place. |
MusicIds | List of audio asset ids for the background music shuffle. | Add or change background music tracks. |
EnvironmentThemes | Selectable environment/scene themes (id, name, preview image, scene name). | Add or rename preview environments. |
GroupRoles | Group rank → role mapping with display name, color, icon, and chat-tag identifier; includes which roles are active. | Define group roles, their chat tags, and colors. (Cosmetic; visible to clients by design.) |
ChatTags | Chat-tag definitions (name, color, gradient) consumed by the chat client. | Customize chat-tag appearance. |
DonationProductIds | Developer-product ids used as donation buttons on the donation board, one per Robux price. | Wire the donation board to your own developer products. |
TextGlyphs | Special glyph characters (Robux, Premium, Verified) used inside rich text. | Change which font glyphs render for these symbols. |
Config into the server Config for security; they are no longer
client-editable from ReplicatedStorage. Categories now reach the client through a RemoteFunction.
10 State management (Charm / CharmSync)
The UI is reactive. State lives in "stores" as Charm signals; UI modules subscribe to those signals and redraw when they change. This avoids manually wiring updates throughout the UI.
Key concepts:
- Signal: a reactive value with read, write, and subscribe.
- Derived value: recomputed from other signals.
- Peek: read a signal without creating a reactive dependency. Used carefully to avoid stale caches in queries.
- Subscribe vs listen: subscribe skips the initial call; listen fires at setup too. Choosing wrong here caused boot-time "auto-save" bugs that were fixed with explicit guards.
CharmSync replicates server state to the client over a dedicated Remote (for example, players' chat tags).
11 Data persistence
Player data (favorites, purchase history, settings, likes, style preference, saved outfits, published codes, stats) is persisted with ProfileStore, which provides safe per-player sessions.
Notable persistence patterns:
- "Push + pull" loading: on profile load the server pushes settings to the client; the client also has a fallback that requests them if they did not arrive in time. A "settings ready" guard prevents the client from overwriting data with defaults during boot.
- Forced writes after changes: in-memory changes are explicitly committed to DataStore when they must persist.
- Deferred flush (anti rate-limit): community outfits live in an in-memory index that is the source of truth; changes mark the index "dirty" and a background loop flushes it periodically in a single operation, with a guaranteed flush on server close. Individual keys that need an immediate lookup are written right away. This pattern avoids saturating the DataStore queue when publishing/deleting in bursts.
- Persistent blocklist: outfits deleted by admins are recorded in a blocklist so they do not reappear when their owner reloads their profile. This list uses the same deferred flush.
12 Client-server communication (Remotes)
The boundary between client and server is the Remotes layer. They are described by purpose (without detailing their payloads).
Catalog and avatar
- Search items, request the next page, get creator groups, and get the catalog categories.
- Save the avatar state, clear it to the original avatar, get the saved description.
- Resolve bundle descriptions and infer bundles from items.
- Toggle and get favorites.
- Play, stop, and resolve emotes (the server returns the animation id to the client).
Purchases and economy
- Single-purchase request and purchase notifications (local and global).
- Bulk purchase and completion notification.
- Bulk item-ownership verification.
- Robux gifting between players (request, confirmation to sender, notice to receiver).
- Price binding for presentation.
Outfits and community
- Generate and look up outfit codes, record wears, toggle a code favorite, list a creator's outfits.
- Paginated queries of community outfits and favorites, with keyword, sort, gender, and creator.
- CRUD operations on personal outfits (save, rename, delete, update, favorite, set gender).
Social and settings
- Verify permission to view another player's outfit; like a player and broadcast the notice.
- Chat tags and system messages.
- Update a setting, receive settings on load (push), and request them (pull).
- Style preference: set, log funnel steps, receive and request the preference.
13 Main data flows
Conceptual descriptions of how information travels in the most important operations.
Catalog search
The UI requests a search → the server queries Roblox's official catalog → it returns paginated results that the catalog grid renders.
Equipping an item
The client avatar state records the change, applies it to the preview, and sends it to the server → the server validates and cleans the description, merges it with the saved one, and applies it to the character queued per player (to avoid race conditions when equipping fast).
Body proportion adjustment
The slider computes scale values that are applied to the preview with throttling, translating scale names into the format the avatar description expects.
Purchase
The UI requests a purchase → the server launches the official Marketplace flow → on completion, the client fires celebration effects (flash, confetti, sounds).
Robux gift
A subscriber requests to gift another player enough for a product → the server uses the official Robux transfer, accounting for the fact that the receiver gets a fraction of the sent amount and that per-transfer minimum and maximum caps exist. Effects and notices fire on confirmation.
Emote
The UI requests an emote → the server resolves the animation id and returns it → the client loads the animation onto the character. Requesting the same emote again stops it.
Undo / redo
The avatar-state history moves a pointer over saved snapshots and notifies change listeners to refresh the view.
Accessory refinement
Selecting an accessory initializes the sliders; dragging previews on the rig; releasing commits the change and persists it.
Save to Roblox (HUD)
The save button resolves item info across several parallel phases (individual products, direct bundles and cache, and name inference) and opens the outfit purchase modal. The reverse bundle lookup, being the slowest phase and dependent on a network proxy, runs in the background with progressive rendering and a server-side time budget.
Community outfits
At startup the server loads the full index of published outfits (from all users, online or not). Publishing adds an entry to the index and writes the lookup key immediately, deferring the index flush. Queries iterate the in-memory index, filter and sort, and paginate.
Chat tags
The server syncs tags to the client (via CharmSync) and the client applies them to messages through the text chat service.
Loading and intro
The intro builds its own screen and, when finished, marks loading as done. The main UI (HUD, style onboarding) waits for that signal before showing.
Hidden players
Depending on the privacy setting, the client makes other players transparent (a local, non-replicated effect) and blocks their selection in popups.
14 Subsystems by feature
Avatar
Central client avatar state, validation rules, character loading, equipped list, body-color panel, scaling panel, accessory refinement, undo/redo, and reset to the default avatar. The actual application of the description to the character happens on the server, queued per player.
Catalog
Open/close controller, client catalog API, paginator, category and subcategory tabs, search bar and filters, query builder, item cards and detail view, inventory, favorites, and featured items.
Outfits
Outfit API, "my outfits" controller and menu, outfit viewer, outfit purchase modal, save-to-Roblox and update menus. Outfits are identified by name; an outfit's "style" is, by convention, a keyword over its name rather than a separate field.
Community
Community outfit browser with sorting (relevance, favorites, popularity, trending, oldest, newest), per-tab gender filters and a creator filter; player popup (view, wear, like); publish-outfit and "match" menus; shareable-code controller. There are also physical stands in the world that display outfits by style preference, with a fallback so they never end up empty.
Emotes
An emotes menu with search and favorites, and a controller that maintains the playback state on the real character.
Purchase
Purchase client (flow, prompts, window state, and post-purchase effects) and bulk purchase. Validation and receipt processing happen on the server.
Gifting
Lets a subscriber gift another player the Robux needed for a product, via the official Robux transfer, with its limits and split. It accounts for the cross-server gift case (the receiver's notice may be generic).
Settings
Settings panel and store with "push + pull" persistence and guards that prevent boot-time overwrites. Covers, among others, outfit visibility, hidden players, and purchase notifications.
Style preference
A one-time onboarding to pick style and gender; gender is stored in one format and translated to another for server queries. It feeds the world outfit stands. It appears after the intro, thanks to the loading-done signal.
Chat and social
Chat tags by group role (synced server→client), per-username name color, system messages, and a player-to-player likes system with persistence and a chat notice.
Interface and intro
The HUD and its buttons, the panel manager (blur, overlay, sounds), the frame router, music, free camera, subscription banner, toasts, and the top bar. The intro lives in the first-loaded root and controls when the interface is revealed.
Administration
A modular command system based on group rank (not gamepass): a router with aliases, a permission manager with per-session rank caching, and a ban service over the official API (cross-server, permanent or temporary). It includes moderation commands (kick, ban, unban) and outfit commands (delete by code or by user), with the persistent blocklist described above. Adding a new command is declarative: create the command file, register it in the module list, and define its minimum rank.
15 Core nodes of the project
The knowledge-graph analysis identifies the most connected abstractions (the "god nodes") — the pieces the rest depends on most. They are the best starting points for understanding the system and the ones that need the most care when changing.
| Piece | Why it is central |
|---|---|
| HUD wiring | Most connected node on the client; bridges catalog, refinements, community, gifting, panels, and more. The effective orchestrator of interaction. |
| Player-data fetch (server) | Entry point to everything persisted; many systems depend on it. |
| Avatar viewport creation | Foundation of every 3D preview (preview, stands, outfit cards). |
| Button-hover helper | Reused by nearly all UI; connects very different features. |
| Preview rig build | Core of the avatar's representation in the preview. |
| List population (menus and items) | Common rendering of outfits and items across several menus. |
The graph detects no import cycles, indicating a clean dependency hierarchy. Extraction was purely structural (no model-token cost).
graphify-out/graph.html and the full report at
graphify-out/GRAPH_REPORT.md.
16 Coding conventions
- One require convention, absolute: from the client, reference through the
AvatarCreatorroot; on the server, server-local modules are referenced through the server root. No cross-package relative paths and no old-structure names. Exception: siblings within the same package may use relative references. - Suffixes by role:
Controller(flow),Panel/Menu(UI),Card/View(presentation),Store(reactive state),Net(Remote definition),Handler(server handler); no suffix for utilities, data, or pure libraries. - Where each new file goes: third-party library → Packages; in-house utility → Lib; Remote definition → Net/Remotes; static data → Config; pure logic → Domain; any feature module → its Feature; shared UI → UIKit.
- Explicit dependency injection: modules receive their dependencies on initialization rather than resolving them globally.
- State-before-functions order in the critical avatar-state module.
- Subscription cleanup: subscriptions always return their cancel function to avoid leaks.
- Extension
.luauexcept the entry points Rojo requires asinit.*.
17 Studio-only dependencies
Some assets are not in the repository because they live in Studio: the assets inside the client root, the world objects (stands, leaderboards, trampolines), and the UI elements inside the player's UI tree. The code references them by path and leaves them intact.
18 Glossary
- Rojo
- Tool that syncs repository code into Roblox Studio.
- ReplicatedStorage
- Roblox container whose contents are replicated to all clients.
- ServerScriptService
- Roblox container whose contents exist only on the server.
- Remote (RemoteEvent / RemoteFunction)
- Roblox's official mechanism for client-server communication.
- Charm
- Reactive state library based on signals.
- Signal
- A reactive value that notifies its observers when it changes.
- ProfileStore
- DataStore wrapper with safe per-player sessions.
- HumanoidDescription
- Roblox object describing an avatar's full appearance.
- Bundle
- A set of catalog items sold together.
- Outfit
- A saved set of an avatar's clothing and configuration.
- God node
- In the knowledge graph, a highly connected piece much of the system depends on.
- Community (of the graph)
- A group of strongly related pieces, detected automatically.
A Appendix — Catalog category schema
Interactive map of the 14 active categories and their subcategories in the Avatar Creator,
mirroring Schema/init.lua. Each entry documents the corresponding
AvatarEditorService enum / filter and the kind of implementation it requires. This is the concrete
schema behind the catalog search flow described above.
All tab (per-type subcategories are commented out
in the source pending FavoritesService). The tree below reflects only what is active.