Skip to main content

Runtime Limits

This page centralizes the runtime limits that matter during authoring. These limits are enforced or validated by the editor/build pipeline and mirror the fixed-size runtime design.

Core Runtime Counts

LimitValue
GameObjects per scene160
Texture assets64
Texture page slots20
Mesh assets192
Material assets64
Audio clips16
Streaming CD-XA music tracks8
Lua Behaviour components32
Script source cache32
UI canvases8
UI elements32
UI text entries32
Bitmap fonts3
Rooms24
Portals48
NavMesh Surfaces4
NavMesh Agents16
Navigation nodes256
Navigation directed links1,024
Cutscene clips8
Tracks per cutscene12
Keyframes per track48
Cutscene keyframes across a scene256
Standalone Animation clips12
Concurrent standalone Animation instances8
Tracks per standalone Animation clip12
Keyframes per standalone Animation track48
Standalone Animation keyframes across a scene256
Animator components per scene32
Animator states per GameObject4
Skinned meshes per scene16
Particles32
Collision pairs64
Manifest assets256
Manifest scripts64
Manifest scenes32

Memory and File Limits

LimitValue
Main RAM2 MB hardware total
VRAM1 MB hardware total
SPU RAM512 KB hardware total
Lua heap runtime cap512 KB
Individual Lua script cap64 KB
Lua instruction budgetPer callback plus a global per-frame budget
Manifest size cap16 KB
Binary scene size cap160 KB
Runtime packet buffer96 KB
Soft packet budget80 KB
Binary scene production target65% of 160 KB
Manifest production target70% of 16 KB
Packet production target80% of 96 KB
Runtime RAM warning floorLess than 160 KB free inside the PS1-safe runtime budget
Runtime RAM release floorLess than 96 KB free blocks release builds

Rendering Limits and Budgets

ItemValue or rule
Ordering table length2048 buckets
UI reserved OT buckets64 foreground buckets
Scene directional lights3
Recommended max triangles per mesh2,000 soft limit
Hard max triangles per mesh2,048; split or sectorize before build
Recommended max scene triangles4,000 soft limit
Recommended far distanceKeep at or below roughly 32 world units when possible
Built-in static bake focusCompatible Plane and Cube geometry

SaveData Limits

LimitValue
Memory-card allocationOne 8,192-byte block
Active memory-card portPort 1
Key/value entries32
Key field32 bytes including terminator
Value field64 bytes including terminator

For PlayerPrefs, use keys up to 28 bytes and string values up to 61 bytes. Both APIs share the 32-entry limit. See Persistence for saving, loading, and handling errors.

NavMesh.FindPath() and runtime NavMesh Agents require the complete route to fit within 32 path points. Longer requests fail instead of returning a partial route. NavMeshAgent queries are restricted to their assigned surface. Navigation graph nodes/links are baked into the scene and count against the .BSN load size as well as their fixed runtime arrays.

Timeline Limits

Only one timeline clip plays at a time. Clip names and track names use the 31-character BSN string field; event payloads use the 63-character field. Validation requires strictly increasing keyframe times within the clip duration.

Standalone Animation Limits

Up to eight standalone instances run concurrently. Active slots are evaluated from 0 through 7, and a later slot wins when two clips write the same property in one frame. OnComplete runs only after natural non-loop completion, never after Stop or a loop wrap.

Standalone clips allow GameObject transform/enabled, UI visibility/progress/color/ position/size, rumble, and skeletal clip-event tracks. Camera, audio, script, and fog tracks remain cutscene-only.

Transform Animator Pool

The Transform Animator component uses a deterministic fixed-capacity pool of 32 rows, with four transform states per row. A row is assigned only when a scene GameObject actually has a Transform Animator, so ordinary GameObjects do not embed four unused state records in every runtime GameObject. Allocation follows deterministic scene-load order and the pool is reset on scene load.

This is still a hard capacity, not dynamically growing desktop memory. The editor and build reject a scene above 32 Transform Animator components, and each component must remain at or below four states.

Skeletal Animation Limits

LimitValue
Bones per asset64
Clips per asset16
Vertices per asset4,096
Indices per asset12,288
Geometry per asset / scene128 KiB / 192 KiB
Shared deformation scratch64 KiB
Matrix data per asset / scene192 KiB / 256 KiB
Clip / bone name24 / 31 characters

The importer uses one dominant bone per vertex and one material per asset. Shared scratch is the largest asset requirement, not a sum per renderer. See Skeletal Animation for the full workflow.

Script Property Limits

LimitValue
Properties per Lua Behaviour component8
Property name length32 bytes
Property value length64 bytes

Runtime String Validation

The build validates fixed-size runtime strings before writing .BSN files. GameObject names, mesh names, material/audio GUIDs, script paths, script properties, input actions, interaction prompts, Interactable button/canvas names, callback function names, Spawn Point IDs, Transform Animator state names, UI Canvas names, and UI text must fit their runtime fields. Overlong values are reported as build issues instead of being silently truncated.

Audio Voice Policy

The PS1 SPU provides 24 hardware voices. Polygon Engine does not silently steal a voice that is already in use; if all voices are occupied, playback fails with an explicit Audio: no channel available runtime error. Keep simultaneous one-shot effects and looping sources within that voice budget.

CD-XA music follows a separate global streaming path and does not occupy one of the 16 resident audio-clip slots. Only one music stream plays at a time. See Audio Import for when to choose each path.

Level Builder Export Limits

LimitRule
Vertex index rangeMust fit 16-bit indices
Long triangle edgesValidate and tessellate when needed
Degenerate facesMust be fixed before reliable export
Non-manifold geometryTreat as a warning/risk for runtime use
Material slotsMaximum 64 per Level Builder mesh
Scene collisionPlaced meshes should keep or tune their generated Collider

How to Use This Page

When a scene grows, check limits in this order:

  1. Run Build > Validate Project.
  2. Open Build > Show PS1 Budget.
  3. Build the project and inspect the post-bake reports in Build/ps1/logs/.
  4. Compare hard errors against this page.
  5. Reduce content, split scenes, simplify assets, or adjust authoring before testing in DuckStation.

If a number here and a validation message disagree, trust the current validation output and update this documentation.

Allocation and numeric domains

Production BSN scenes allocate storage for their actual GameObject count, at 1,552 bytes per runtime record, while retaining the 160-object cap. This record cost is additional to referenced mesh, Lua, and optional feature allocations. Debug's legacy loose-JSON path can reserve full entity capacity. Built-in mesh buffers retain only the generated geometry size; their subdivision limits have not increased.

There are at most 16 authored Particle System emitters, sharing the 32-particle runtime pool. Input Action Maps contain at most 32 actions. String-field capacities include their terminating byte: script-property names hold at most 31 bytes and values at most 63. These are byte limits, not Unicode character counts.

World position uses signed 32-bit values at 1,024 units per world unit. Transform scale uses a separate signed 16-bit conversion with magnitude at most 32767 / 1024. Mesh-local bake/projection representations use other scales; do not substitute a vertex-bake constant for the world-coordinate contract. Setters and build validation reject non-finite and out-of-range data.

The 80 KiB soft packet budget is distinct from Production Acceptance's 80% of 96 KiB target. Memory quantities displayed as KB in the editor use 1,024-byte units. See Budget Dashboard for load-peak accounting and the scope of PS1 Ready.