Skip to content

The Inventory

You’ve transformed a Part. The particles fly. They don’t yet have a texture — they’re rendering as flat solid-coloured squares.

You could go to the Roblox library, find a free fire spark, copy its asset id, paste the id into the Render Template’s Decal, repeat for the next emitter. After ten emitters you’ve spent a half-hour copying asset ids and your enthusiasm has drained.

The Inventory is the plugin’s answer to that grind. It’s a pair of side-by-side texture libraries you can browse, search, filter, and click-to-apply onto whatever’s selected. The Inventory library is curated by the plugin and shipped with your Studio install — about 12,000 textures and a few hundred animated flipbooks across the keyword categories the VFX-modelling community has converged on. The Local Inventory library is yours: textures you’ve uploaded, flipbooks you’ve assembled or dissected, even fully-serialized Roblox instances you’ve saved for re-insertion later.

This chapter walks the entire system: the panel layout, the click-to-apply behaviour, the selection toolbar, the detail view, batch upload, spritesheet dissection, asset serialisation, and the JSON export-import format that lets you share libraries with other plugin users.

The plugin HUD has an Inventory toggle button. Click it. A 600×380 panel opens. You can drag it by its title bar and resize it from any edge — the grid auto-reflows the column count when you make it wider.

A second toggle in the QMenu turns it back off; so does the close button on the panel itself. The visibility persists across plugin re-toggles, so once you’ve opened it on a workstation it stays open the next time you launch Studio.

The left edge of the panel is a sidebar with two collapsible sections.

Inventory — the cloud-fed catalogue. Three sub-modes: Textures (single-image assets, ~12,000 entries), MeshFlipbooks (named animation sequences, each one a list of texture ids that play as frames), and Assets (full Roblox instance trees you can re-insert into your place). All three are fetched on first panel-open from the plugin’s GitHub-hosted JSON files, cached in memory for the rest of the session.

Local Inventory — yours. Same three sub-modes mirrored: Textures, MeshFlipbooks, Assets. Anything you upload, dissect, import from the workspace, or paste in via the JSON import dialog lands here. Persists across plugin restarts (stored in plugin:GetSetting("LocalInventory")) and survives plugin replacement (a backup ModuleScript is also written to ServerStorage).

Click any of the six sub-mode buttons and the grid updates to show that library. The active button highlights gold; the others stay muted. The two parent headers — Inventory and Local Inventory — fold open and shut on click, so you can collapse the half you’re not using.

Above the grid, a row of tabs filters what’s shown without changing modes:

  • Static — only entries with no flipbook grid attribute.
  • Flipbook — only entries whose source data declares a flipbook grid.
  • Favourites — only entries you’ve starred (per-entry, see the Detail panel below).
  • Recents — the last 20 entries you inserted, newest first. Persists across plugin restarts.

Tabs are visible only when they’re meaningful for the current mode. Inventory.Textures shows all four; Inventory.MeshFlipbooks shows only Favourites and Recents (Static and Flipbook don’t apply); the four Local sub-modes hide tabs entirely (Local libraries are too small to need filtering this way).

The search box appears at the top of the content area, beside the Filters dropdown. Type a few letters and the grid filters live. Matching is debounced so a fast typist doesn’t trigger a re-render on every keystroke.

The search index is built once per entry from name, asset id, and any keyword tags. Multiple words are AND-ed: typing red fire shows only entries whose _search field contains both red and fire, in any order. Empty the box and the unfiltered grid returns.

Click the Filters ▼ button (next to the search box) and a dropdown appears with the categories that apply to the current library:

  • Inventory.Textures — the keyword categories the curated library is grouped into. The plugin clusters them into five parent groups (Shapes, Combat, Elements, Radiance, Misc) plus an Other bucket for any uncategorised keyword.
  • Inventory.Assets and Local Inventory.Assets — the asset’s ClassName (every distinct ClassName found across the cached asset list), plus a Favourites toggle.
  • Local.Textures and Local.MeshFlipbooks — just Favourites (no keyword tagging on user-uploaded textures yet).

Filters are multi-select: click Fire, then click Lightning, and the grid shows entries tagged with either keyword. Click the tag again to deselect. The All button at the top of the dropdown clears every active filter at once.

The content area is a grid of small thumbnails. Each cell shows the entry’s preview image (or a 3D rendering for asset entries — see Asset detail below), the entry’s name underneath, and a small star icon for favouriting at hover.

The grid renders only what’s currently visible plus a small buffer, so scrolling stays smooth even at 12,000 entries.

Hover a cell to see its name in a tooltip. Single-click a cell to open the Detail panel beside the inventory. Shift-click to add it to a multi-selection.

Selection — Shift+click and paint-select

Section titled “Selection — Shift+click and paint-select”

Multi-selection is how you act on more than one entry at a time. Two ways to build a selection:

Shift+click — toggles the clicked cell’s membership. Selected cells get a thin gold stroke around the thumbnail and a white-on-gold checkmark in the top-right corner.

Shift+drag — paint-select. Hold Shift, press the mouse down on a cell, and drag across other cells. The first cell’s pre-drag state determines the paint mode: starting on an unselected cell paints “select” across every cell you cross; starting on a selected cell paints “deselect.” Releasing the mouse stops the drag. This is for sweeping across many cells in one motion when you don’t want to Shift+click each one individually.

The selection has a soft ceiling driven by Roblox’s TextBox character limit (one of the things you can do with a selection is export it as text — see Export and Import JSON below). When you hit the ceiling, the selection toolbar shows (MAX) next to the count and refuses to add more entries until you remove some.

As soon as one cell is selected, an inline toolbar appears in the top-right of the panel showing the count and four action buttons: Export, Dissect, Delete, Clear. The toolbar is selection-only; it disappears the moment you clear.

  • Export — packages the selection into the JSON format and opens the Export/Import dialog, pre-filled with the JSON text. Useful for sharing a curated subset rather than your whole library. Compression kicks in automatically when the raw JSON would exceed Roblox’s TextBox limit.
  • Dissect — fills the Dissect Spritesheets dialog’s id box with the asset ids of every plain-texture entry you’ve selected, ready to dissect them all at once. Hidden when no plain-texture entry is in the selection (Dissect doesn’t apply to flipbooks or assets).
  • Delete — removes selected entries from your Local library. No confirmation prompt; the toolbar’s Clear button is the recovery if you misclick. Cloud entries can’t be deleted (they’re shipped with the plugin).
  • Clear — empties the selection without doing anything else.

Note what’s not on this toolbar: there’s no Favourite button. Favouriting is a per-entry action exposed in the Detail panel, not a multi-select toolbar action. The plugin treats favourites as a “this one specifically matters” annotation, not a bulk operation.

Single-click any cell and a Detail panel slides in beside the inventory (right side if there’s room, left side if the inventory is too far right on your screen). It shows the full entry and is where every per-entry action sits.

For texture entries:

  • Preview image at full resolution. If the entry is a spritesheet (its data declares a grid count), the preview animates: the plugin offsets the visible rect frame by frame at 24 fps, cycling through every cell of the grid. This is the cheapest “is this flipbook actually animated?” check you can run.
  • Name — editable for Local entries (an edit-pencil icon appears beside the name when it’s editable; cloud names are read-only).
  • Asset ID — read-only, with a Copy button next to it that selects the textbox text so you can Ctrl+C it.
  • Keywords — comma-joined list of the entry’s tags.
  • Info — single-line summary like 8x8 flipbook, 1024px, 24 fps or Static texture.
  • Grid — for Local entries only, lets you toggle a flat texture into a spritesheet by typing a grid count (e.g. 4 or 8x8). The detail panel’s animated preview updates to match.
  • Favourite button — toggles favouriting. Persists via plugin settings (InventoryFavourites).
  • Insert button — applies the entry to whatever’s selected in Studio (see Insert behaviour below).
  • Delete button — Local entries only. Removes from the library and refreshes the grid.

For MeshFlipbook entries:

  • Animated preview — the first texture id is shown, then the next, then the next, cycling at 24 fps. The plugin pre-loads each frame in batches of six via ContentProvider:PreloadAsync so the animation doesn’t stutter on first cycle.
  • Frames count, Texture IDs preview (first three plus an ellipsis), Info showing the frame count.

For Asset entries:

  • 3D viewport — a ViewportFrame showing the deserialized instance tree, slowly rotating around the bounding box. The plugin caches up to 20 deserialized previews and recycles them LRU as you click through different assets.
  • Class and Location fields — what the asset is and where it came from (a stored parent path, multiple paths, or just the compressed data size if no path was captured).

Clicking Insert in the Detail panel (or pressing Enter while the panel is focused) applies the entry to your current Studio selection.

The plugin walks each part of the selection and routes by type:

  • Transformed Parts — sets the Decal.Texture (or Texture.Texture) on the part’s RenderTemplate. If it’s a flipbook entry, repopulates the part’s MeshFlipbooks folder with new Decals.
  • Transformed Beams — sets the Beam’s RenderTemplate.Texture. Flipbooks repopulate BeamFlipbooks.
  • Transformed Trails — sets the Trail’s Texture directly.
  • Transformed ImageLabels — sets the Image on both the live ImageLabel and its RenderTemplate. If the entry is a spritesheet (declared grid), the plugin auto-flips the ImageLabel into Spritesheet mode and writes GridCols/GridRows to match.
  • Native ParticleEmitters (raw Roblox emitters, no plugin transform) — sets Texture, and if the entry has a 2/4/8 grid, sets FlipbookLayout to the matching Grid2x2 / Grid4x4 / Grid8x8 enum.
  • Selected Decals or Textures directly (not on any transformed parent) — sets Texture straight onto them.

Whatever you inserted is also pinned via the plugin’s Texture Pinning so the asset stays GPU-resident — the first emit after an insert won’t stall on texture decode.

For Asset entries, Insert is different: the plugin deserializes the stored data into one or more instances and parents each to the path it was originally captured from. If the original path no longer exists in your place — say, the asset was captured from a ServerStorage.SwordEffects folder that doesn’t exist in this place — the plugin shows a red error frame with a Create & Insert button that auto-creates the missing folders before inserting.

Local Textures — single and batch upload

Section titled “Local Textures — single and batch upload”

Switch to Local Inventory.Textures and the toolbar above the grid shows an Upload button. Click it; Studio’s file-picker opens; pick one or more PNGs / JPGs / BMPs / TGAs.

Each file becomes a separate texture entry. The plugin uploads via Studio’s built-in upload path (your own account does the upload — no external API setup needed), gets back a fresh asset id, and adds the entry to your Local library with the file’s name as the entry name.

Uploads are batched. A small status panel docks beside the inventory showing live progress for each file. Failed uploads (network glitch, rate-limit, asset-moderation hold) are retried automatically with backoff between attempts; the status row shows retry countdowns so you know what’s happening. Successes turn the row green; failures turn it red and the file is skipped.

Because uploads share a single batch flush at the end (rather than writing to disk after each upload), large batches don’t thrash the plugin-settings persistence. The status row stays open for three seconds after the batch finishes, then auto-dismisses.

Import Selected — pull textures from your scene

Section titled “Import Selected — pull textures from your scene”

Beside Upload is an Import Selected button. It’s the shortcut for “I already have these textures sitting on Decals / Textures / Beams in my place, and I want them in my Local library without re-uploading.”

Select one or more Decals, Textures, Beams, or any other instance with a .Texture property in Studio’s Explorer. Click Import Selected. The plugin walks the selection, pulls the asset id out of each one’s Texture field, and adds each as a Local Texture entry (using the instance’s Name as the entry name). Existing-id duplicates are skipped silently.

Useful when migrating an old VFX rig into the inventory, or when a free-model asset has textures you want to re-use across other emitters in your project.

Local MeshFlipbooks — name-prompted batch upload

Section titled “Local MeshFlipbooks — name-prompted batch upload”

Same flow as Textures, with one difference: when you’re in Local.MeshFlipbooks and click Upload, the plugin prompts for a flipbook name before it begins. Every file you picked becomes a frame of one flipbook with that name, in pick order, rather than separate texture entries.

This is the manual path to a custom flipbook: render your animation in Blender or whatever your DCC is, export each frame as a PNG, pick them all in the file dialog in correct order, type a name, hit OK.

Frames upload one at a time with the same retry logic as single-texture upload, and the resulting flipbook entry is named whatever you typed.

Import Selected — folders of numbered Decals into flipbooks

Section titled “Import Selected — folders of numbered Decals into flipbooks”

The Local.MeshFlipbooks toolbar also has an Import Selected button, but the input it expects is different: select one or more Folders in Studio’s Explorer, where each folder contains numbered Decals (1, 2, 3, …) pointing at the frames of an animation.

Click Import Selected. The plugin walks each selected folder, reads every child’s .Texture, sorts them by numeric name, and adds one flipbook entry per folder named after the folder. Folders with no .Texture-bearing children are skipped.

This is the right shortcut when you’ve already assembled a flipbook in your scene (or pulled one out of a MeshFlipbooks child on a transformed Part) and want it back in the inventory for re-use.

If your animation source is a single spritesheet — one image laid out as an N×M grid of frames — you don’t need to pre-cut it in an external tool. The plugin will dissect it for you, upload each frame as a separate texture, and stitch the results into a flipbook entry.

Click Dissect in the Local.MeshFlipbooks toolbar (or click Dissect on a multi-selection of plain textures). A dialog opens with three controls and four input paths:

  • Cols and Rows — the grid dimensions of each spritesheet you’ll dissect. Default 4×4.
  • Asset IDs — one per line, or comma-separated. Each id becomes a flipbook named Spritesheet_<id>.
  • From Selection — fills the id box with the .Texture field of every selected Decal/Texture/Beam in your Studio workspace.
  • Upload & Dissect — opens the file picker, uploads each picked PNG/JPG/etc as a single image first (so you have full-res source), then dissects each one with the same Cols×Rows.
  • Dissect — runs the dissection on the assets in the id box.

The dissection itself is parallel — every queued spritesheet runs as its own task. Frame uploads have a small pause between them to stay clear of AssetService rate limits. For assets you don’t own, plugin-side server infrastructure handles the heavy lifting so the result still lands at full source resolution. If that path is unavailable, the tool falls back to a low-resolution thumbnail (guaranteed to work but visibly blurrier).

A status row stays open for each in-flight dissection, showing Frame N/M progress and per-frame retry countdowns when an upload fails.

Local Assets — Studio instances as inventory entries

Section titled “Local Assets — Studio instances as inventory entries”

The third Local sub-mode, Local Inventory.Assets, is where you save Roblox instances themselves — Models, Folders, transformed emitter rigs — as inventory entries you can re-insert later.

To save an asset:

  1. Select one or more instances in the Studio explorer.
  2. Switch to Local.Assets and click Import Selected in the toolbar.

The plugin runs each selection through Roblox’s instance-serialization API, compresses + encodes the result for compact storage, and stores the resulting string in your Local library. The entry’s name is the instance’s name (with (2), (3) suffixes if there’s already an entry with that name), the entry’s class is the instance’s ClassName, and the entry remembers what parent path it came from.

The arrow beside Import Selected opens a small dropdown with two checkboxes that change how the import works:

  • Bundle (import as one) — when ON, every selected instance is serialized together as a single asset entry. Re-inserting the entry restores the whole group at once. When OFF, each selected instance becomes its own entry — useful when you’ve selected ten unrelated effects and want each one available as a stand-alone library item.
  • Paths (save locations) — when ON, each instance’s original parent path is stored alongside the data. On re-insert the plugin tries to put each one back into the same path it came from, with a Create & Insert prompt if the path no longer exists. When OFF, no path is stored and re-insert just drops the instance under workspace.

The toolbar’s button text updates live to reflect the toggles: Import Selected with both off, Import Bundle with only Bundle on, Import + Paths with only Paths on, Import Bundle + Paths with both on. Both default to off — the plain “one entry per selected item, no path tracking” import is the safe baseline.

Pick the combination that matches what you want to be able to re-insert later. A full sword’s-worth of emitters that should always come back together → Bundle. A cleanup pass on a complex place where each rig should re-appear in its original folder → Paths. Both → re-inserting the bundle puts every instance back exactly where it came from.

The Detail panel for an Asset entry shows a 3D ViewportFrame previewing the instance tree, slowly rotating. The plugin caches recently-viewed previews so clicking back to one is instant; clicking a fresh asset queues a deserialize task. Deserialization is throttled to keep the UI responsive.

Export and Import JSON — sharing libraries

Section titled “Export and Import JSON — sharing libraries”

Every Local inventory entry can be exported as JSON. Click the button (Export/Import) in the Local toolbar and a dialog opens with a multi-line text box and two buttons: Export and Import.

Export writes your full Local library to the text box as JSON: textures, flipbooks, and assets all in one document. If the JSON exceeds Roblox’s TextBox character limit, the plugin automatically compresses it and tags the result with a recognizable prefix so the importer knows what it’s looking at. Compression typically shrinks a serialized library to a fraction of its raw size.

Import reverses the flow. Paste a JSON string (compressed or raw) into the text box, click Import, and the plugin merges the entries into your Local library. Duplicates are skipped silently — adding the same texture id twice doesn’t double-list it; adding a flipbook with a name that already exists either skips the duplicate (when the textures match) or appends a (2) suffix (when the textures differ).

The selection toolbar’s Export button does the same export for a subset — only the entries you’ve selected. This is the path for sharing a curated effect kit with another plugin user: select fifty textures and three flipbooks, click Export, copy the JSON text, send it through whatever channel you like, recipient pastes it into their Import box.

The dialog’s text box also accepts an alternate input source: if you’ve left an empty text box and click Import, the plugin scans the entire DataModel for any LuaSourceContainer (a Script, LocalScript, or ModuleScript) carrying the attribute ExportPart_Icle and reads the source. This is for sharing libraries via published-place attachments — anyone who joins the place gets the export script, runs Import, and the library lands in their own plugin settings. The plugin tags the source script with a per-user attribute so each Studio install only imports it once.

The Recents tab on the Inventory.Textures (and Inventory.MeshFlipbooks) mode shows the last 20 entries you inserted, newest first. Persisted via plugin:GetSetting("InventoryRecents") — survives plugin restarts and Studio restarts.

This is the fastest way to re-find a texture you used three minutes ago without remembering its name. The list grows up to 20 entries and then drops the oldest each time you insert something new.

Local inventory data is stored two places:

  1. plugin:GetSetting("LocalInventory") — the canonical storage. Roblox’s plugin-settings system. Survives plugin re-toggles and Studio restarts.
  2. A hidden ServerStorage ModuleScript — a backup copy written every time the canonical save runs. Survives plugin replacement: if you reinstall the plugin from scratch (and lose the plugin settings), the backup is still in your place file and gets restored on first init.

Texture-id favourites and recents have their own settings keys (InventoryFavourites and InventoryRecents).

The implication: a place file with a populated Local Inventory carries the inventory with it. Open the place on a different workstation, the plugin restores the backup; you have your library on both. This also means that if you publish a place, anyone who edits a copy of it sees your Local Inventory restored from the backup ModuleScript.

A handful of behaviours that don’t fit anywhere else.

Favourites are per-entry, not per-selection. The selection toolbar has Export, Dissect, Delete, and Clear — there’s no bulk Favourite button. Favouriting is a single-entry annotation done from the Detail panel.

Cloud entries can be favourited too. The Favourites tab pulls from the same favourite-set across all libraries (cloud and local), so a starred cloud texture appears in Favourites alongside any Local favourites.

The 200K TextBox cap drives the export-compression behaviour. Roblox’s TextBox can hold about 200,000 characters before performance falls off. The selection-size estimator caps at 190K, the compression kicks in around 199K, and the importer transparently handles both compressed (PICZ:-prefixed) and uncompressed strings.

Asset previews are 3D, not 2D. Asset entries show their actual instance tree in a ViewportFrame rather than a flat thumbnail. The viewport rotates slowly so you can see the geometry from all sides. This is a heavy preview — the plugin caps live previews at two simultaneous deserializations and 20 cached entries, and aggressively clears them when you switch out of asset mode.

Dissect handles assets you don’t own through plugin-side server infrastructure. Without that infrastructure, dissecting unfamiliar assets would be capped at thumbnail resolution by Roblox’s API.

Inventory loading is deferred two seconds after plugin start. The plugin’s UI mounts first; the heavy JSON fetch + parse for the 12,000-entry catalogue runs after a task.wait(2). This is why the inventory panel sometimes shows “Loading…” for a beat the first time you open it on Studio start.

Bulk JSON import is fast enough that you don’t need to babysit it. A 500-entry Local library used to take several seconds to import; the import path now finishes sub-second on most batches. Paste the JSON, click Import, and the panel refreshes by the time you let go of the button.

Names with commas persist correctly. Earlier versions tripped over comma-bearing entry names in Favourites and Recents (the comma collided with the serialisation format). That’s fixed — name an entry Lightning, Blue and it survives both lists.

You’ve now seen the inventory’s full surface area: two libraries, six modes, four tabs, search, multi-select, JSON share, batch upload, three dissect paths, asset serialization. The next chapter — The Theme Editor — covers retinting the plugin’s panels (nine built-in palettes, custom mode, live preview).