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
| Limit | Value |
|---|---|
| GameObjects per scene | 160 |
| Texture assets | 64 |
| Texture page slots | 20 |
| Mesh assets | 192 |
| Material assets | 64 |
| Audio clips | 16 |
| Streaming CD-XA music tracks | 8 |
| Lua Behaviour components | 32 |
| Script source cache | 32 |
| UI canvases | 8 |
| UI elements | 32 |
| UI text entries | 32 |
| Bitmap fonts | 3 |
| Rooms | 24 |
| Portals | 48 |
| NavMesh Surfaces | 4 |
| NavMesh Agents | 16 |
| Navigation nodes | 256 |
| Navigation directed links | 1,024 |
| Cutscene clips | 8 |
| Tracks per cutscene | 12 |
| Keyframes per track | 48 |
| Cutscene keyframes across a scene | 256 |
| Standalone Animation clips | 12 |
| Concurrent standalone Animation instances | 8 |
| Tracks per standalone Animation clip | 12 |
| Keyframes per standalone Animation track | 48 |
| Standalone Animation keyframes across a scene | 256 |
| Animator components per scene | 32 |
| Animator states per GameObject | 4 |
| Skinned meshes per scene | 16 |
| Particles | 32 |
| Collision pairs | 64 |
| Manifest assets | 256 |
| Manifest scripts | 64 |
| Manifest scenes | 32 |
Memory and File Limits
| Limit | Value |
|---|---|
| Main RAM | 2 MB hardware total |
| VRAM | 1 MB hardware total |
| SPU RAM | 512 KB hardware total |
| Lua heap runtime cap | 512 KB |
| Individual Lua script cap | 64 KB |
| Lua instruction budget | Per callback plus a global per-frame budget |
| Manifest size cap | 16 KB |
| Binary scene size cap | 160 KB |
| Runtime packet buffer | 96 KB |
| Soft packet budget | 80 KB |
| Binary scene production target | 65% of 160 KB |
| Manifest production target | 70% of 16 KB |
| Packet production target | 80% of 96 KB |
| Runtime RAM warning floor | Less than 160 KB free inside the PS1-safe runtime budget |
| Runtime RAM release floor | Less than 96 KB free blocks release builds |
Rendering Limits and Budgets
| Item | Value or rule |
|---|---|
| Ordering table length | 2048 buckets |
| UI reserved OT buckets | 64 foreground buckets |
| Scene directional lights | 3 |
| Recommended max triangles per mesh | 2,000 soft limit |
| Hard max triangles per mesh | 2,048; split or sectorize before build |
| Recommended max scene triangles | 4,000 soft limit |
| Recommended far distance | Keep at or below roughly 32 world units when possible |
| Built-in static bake focus | Compatible Plane and Cube geometry |
SaveData Limits
| Limit | Value |
|---|---|
| Memory-card allocation | One 8,192-byte block |
| Active memory-card port | Port 1 |
| Key/value entries | 32 |
| Key field | 32 bytes including terminator |
| Value field | 64 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.
Navigation Path Limits
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
| Limit | Value |
|---|---|
| Bones per asset | 64 |
| Clips per asset | 16 |
| Vertices per asset | 4,096 |
| Indices per asset | 12,288 |
| Geometry per asset / scene | 128 KiB / 192 KiB |
| Shared deformation scratch | 64 KiB |
| Matrix data per asset / scene | 192 KiB / 256 KiB |
| Clip / bone name | 24 / 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
| Limit | Value |
|---|---|
| Properties per Lua Behaviour component | 8 |
| Property name length | 32 bytes |
| Property value length | 64 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
| Limit | Rule |
|---|---|
| Vertex index range | Must fit 16-bit indices |
| Long triangle edges | Validate and tessellate when needed |
| Degenerate faces | Must be fixed before reliable export |
| Non-manifold geometry | Treat as a warning/risk for runtime use |
| Material slots | Maximum 64 per Level Builder mesh |
| Scene collision | Placed meshes should keep or tune their generated Collider |
How to Use This Page
When a scene grows, check limits in this order:
- Run Build > Validate Project.
- Open Build > Show PS1 Budget.
- Build the project and inspect the post-bake reports in
Build/ps1/logs/. - Compare hard errors against this page.
- 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.