Understanding HoloScript's runtime limits helps you build performant applications.
Runtime Limits (All Formats)
These limits apply regardless of which file format you use:
Constraint
Value
Notes
Max Nodes
1,000
THE critical limit
Max Execution Time
5 seconds
Per frame/update
Max Depth
50 levels
Nesting depth
Max Call Stack
100
Function call depth
Max Particles
1,000/system
Per particle system
::: warning Critical Limit
The 1,000 node limit is the primary constraint. Design around lazy loading and object pooling to maximize capability.
:::
Limit
Value
Max File Size
50KB code
Max Blocks
100 per file
Max Nesting Depth
10 levels
Keywords
100+ available
Limit
Value
VR Traits
57 implemented (119+ defined)
Lifecycle Hooks
80+ available
Scale Range
0.1x - 10x
Inventory Stack
10 items
Limit
Value
Compositions
1 per file
Templates
Unlimited
Objects
Unlimited
Nesting
Unlimited
When using all formats together:
Capability
Combined Value
VR Traits
see docs/NUMBERS.md for current count (stackable - 20+ per object)
Lifecycle Hooks
80+ (combinable)
Builtin Functions
90+
Import Chains
Unlimited
Nesting Depth
Unlimited (AST level)
COMBINED = .holo(∞ scene) × .hsplus(traits + 80 hooks) × .ts(logic)
PRACTICAL_CAP = 1,000 nodes OR 5 seconds (whichever hits first)
// ❌ Bad: Creates 10,000 nodes at once
@for x in range(0, 100) {
@for y in range(0, 100) {
object "Tile_${x}_${y}" { position: [x, 0, y] }
}
}
// ✅ Good: Lazy loading with chunks
spatial_group "World" {
@lazy_load(chunk_size: 100, view_distance: 50)
@for x in range(0, 100) {
@for y in range(0, 100) {
object "Tile_${x}_${y}" { position: [x, 0, y] }
}
}
}
// Create a pool of reusable objects
@pool("Bullet", capacity: 50)
function fire() {
let bullet = pool_get("Bullet")
bullet.position = gun.position
bullet.velocity = aim_direction * 100
delay 3s then {
pool_return("Bullet", bullet)
}
}
object "DetailedBuilding" {
@lod(
near: { model: "building_high.glb", max_distance: 20 },
mid: { model: "building_med.glb", max_distance: 100 },
far: { model: "building_low.glb", max_distance: 500 },
cull: { min_distance: 500 }
)
}
composition distantObject {
@physics(enabled: distance_to_player < 50)
@collidable(enabled: distance_to_player < 30)
}
@debug_overlay {
show_node_count: true
show_frame_time: true
show_memory: true
}
logic {
@profile("SpawnWave") {
spawn_wave()
}
when node_count > 800 {
warn("Approaching node limit!")
cleanup_distant_objects()
}
}
Platform
Recommended Nodes
Notes
Quest 2
500
Mobile GPU
Quest 3
700
Better GPU
PCVR
1,000
Full limit
Web
800
Browser overhead
Mobile AR
400
Battery/thermal
Resource
Typical Size
Limit
Scene graph
1-10 MB
50 MB
Textures
5-50 MB
200 MB
Audio
1-20 MB
50 MB
Models
5-100 MB
500 MB
// Recommended texture sizes
object "Optimized" {
// Mobile/Quest
texture: "texture_1k.jpg" // 1024x1024
// PCVR
texture: "texture_2k.jpg" // 2048x2048
// Use compressed formats
texture: "texture.ktx2" // GPU-compressed
}
The 1,000 node runtime limit is the only practical constraint. Everything else (imports, nesting, traits) is unlimited or very high. Design around lazy loading and object pooling to maximize capability.