Quick answer: Profile first with Shift+F1 to find whether the bottleneck is CPU, GPU, or memory. Reduce active object counts by destroying or deactivating off-screen objects. Use containers to link related objects. Replace “Every tick” conditions with timers where possible. Enable worker mode to run logic on a separate thread. Optimize textures by using power-of-two sizes and reducing sprite sheet resolution for mobile targets.
Construct 3 can handle large projects, but web technology has real constraints. A project that runs at 60 FPS with 50 objects on screen can drop to 30 FPS with 500 objects if you have not optimized your event sheets, rendering pipeline, and memory usage. The good news is that most performance problems come from a small number of common mistakes, and fixing them is straightforward once you know where to look.
Profiling: Find the Bottleneck First
Before optimizing anything, you need to know what is slow. Construct 3 has a built-in profiler you can open by pressing Shift+F1 during preview. This shows you:
• CPU time: How long event sheet logic, behaviors, and physics take per frame
• GPU/Render time: How long drawing takes, including effects and compositing
• Object count: Total active objects in the current layout
• Estimated image memory: How much VRAM your textures are using
The profiler also breaks down CPU time by event group, letting you see exactly which group of events is the most expensive. This is why organizing your events into named groups is important—not just for readability, but for profiling.
You can also use the browser’s built-in DevTools performance panel. In Chrome, press F12, go to the Performance tab, record a few seconds of gameplay, and examine the flame chart. This shows you JavaScript execution time at a granular level and can reveal issues that the Construct 3 profiler does not surface, like garbage collection pauses.
Reducing Object Count
Every active object in Construct 3 costs CPU time for behavior processing, collision checks, and rendering. The single most impactful optimization for most projects is reducing how many objects are active at any time.
Destroy off-screen objects: Bullets, particles, and spawned effects that leave the viewport should be destroyed. Use the Destroy Outside Layout behavior on projectiles, or manually destroy objects when they move beyond the visible area.
Object pooling: Instead of creating and destroying objects repeatedly (which triggers garbage collection), recycle them. Move “destroyed” objects off-screen and reposition them when needed:
// Simple object pooling in scripting
const bulletPool = [];
function getBullet() {
// Reuse from pool if available
for (const b of bulletPool) {
if (!b.instVars.active) {
b.instVars.active = true;
b.isVisible = true;
return b;
}
}
// No pooled bullet available, create new
const b = runtime.objects.Bullet.createInstance(
"Game", 0, 0
);
b.instVars.active = true;
bulletPool.push(b);
return b;
}
function recycleBullet(b) {
b.instVars.active = false;
b.isVisible = false;
b.x = -1000;
b.y = -1000;
b.behaviors.Bullet.enabled = false;
}
Use tilemaps instead of sprites: A tilemap with 10,000 tiles counts as a single object for rendering purposes. If you are placing individual sprite instances for ground, walls, or background decoration, switch to a tilemap. The difference can be enormous—from thousands of objects to one.
Efficient Event Sheets
Event sheets run every tick (typically 60 times per second). Every condition and action has a cost, and that cost multiplies across hundreds of events and thousands of object instances.
Avoid unnecessary “Every tick” conditions. Many things do not need to be checked 60 times per second. Enemy AI can recalculate paths once per second. UI updates can run every 0.1 seconds. Use the Every X seconds condition instead:
• Bad: Every tick → Set ScoreText to "Score: " & Score
• Better: Every 0.1 seconds → Set ScoreText to "Score: " & Score
• Best: On Score changed (using a function or trigger) → Set ScoreText
Order conditions from cheapest to most expensive. Construct 3 evaluates conditions left to right and top to bottom. Put fast checks (variable comparisons, boolean flags) before expensive checks (collision tests, line of sight):
• Bad: Enemy overlaps Player + Enemy.state = "attack"
• Good: Enemy.state = "attack" + Enemy overlaps Player
The second version skips the collision check entirely for enemies that are not in the attack state.
Use families for shared logic. If you have 10 enemy types that all share the same damage logic, create a Family containing all of them and write the event once against the family instead of 10 times against individual types.
Containers for Composite Objects
Containers are one of Construct 3’s most powerful and underused features. A container links multiple object types so they are created, destroyed, and picked together automatically.
For example, an enemy might consist of:
• The main sprite (with collision and behaviors)
• A health bar sprite
• A shadow sprite
• A detection radius sprite
Without containers, every time you pick an enemy, you need additional events to pick the associated health bar, shadow, and detection radius by UID. With containers, picking any one object automatically picks the matching instances of all container members.
To set up a container, select the main object, go to Properties, and add container members. Then in event sheets, any condition that picks one container member automatically picks the corresponding instances of all other members.
Rendering and Texture Optimization
Rendering performance comes down to draw calls, texture memory, and effect complexity.
Minimize layers with “Force own texture”. Each layer with this property enabled requires a separate render target and compositing pass. Only use it when you need layer-level opacity, blend modes, or effects. For most layers, leave it off.
Reduce effects and blend modes. WebGL effects (blur, glow, distortion) are GPU-intensive. Each effect on an object adds a draw call and potentially a render target switch. Use effects sparingly and disable them on low-end devices:
// Disable effects on low-end devices
function applyQualitySettings(quality) {
const layers = runtime.layout.getAllLayers();
if (quality === "low") {
// Disable all layer effects
for (const layer of layers) {
for (let i = 0; i < layer.effectCount; i++) {
layer.setEffectEnabled(i, false);
}
}
// Reduce particle counts
for (const p of runtime.objects.Particles.getAllInstances()) {
p.behaviors.Particles.rate *= 0.3;
}
}
}
// Detect low-end device by measuring initial FPS
let frameCount = 0;
let startTime = Date.now();
runtime.addEventListener("tick", () => {
frameCount++;
if (frameCount === 120) {
const elapsed = (Date.now() - startTime) / 1000;
const avgFps = 120 / elapsed;
if (avgFps < 45) {
applyQualitySettings("low");
}
}
});
Optimize texture memory. Every unique image in your project occupies GPU memory. Sprite sheets are packed into power-of-two textures (512x512, 1024x1024, 2048x2048). Tips for reducing texture memory:
• Use the Downscaling project property for mobile exports to halve texture resolution automatically
• Avoid huge sprite sheets for objects that only appear once
• Reuse animations across objects where possible (use the same sprite with different instance variables)
• Check estimated image memory in the profiler and aim for under 256 MB for mobile targets
Worker Mode
Worker mode is Construct 3’s most significant architectural optimization. It moves all game logic (events, behaviors, expressions) to a Web Worker thread, freeing the main thread for rendering. This effectively gives you two CPU cores instead of one.
Enable worker mode in Project Properties → Advanced → Use worker. It is on by default for new projects.
Caveats with worker mode:
• DOM access is not available in worker threads. If you use scripting that accesses document or window, it will fail. Use the runtime messaging API to communicate with the main thread instead.
• Some third-party plugins do not support worker mode. Check plugin documentation before enabling it.
• Test thoroughly after enabling worker mode. The timing of some operations changes slightly when logic and rendering run on separate threads.
Related Issues
If you are experiencing low FPS and lag, see Fix Construct 3 Performance Low FPS Lag. For WebGL context lost errors on mobile, see Fix Construct 3 WebGL Context Lost Error. For sprite flickering on mobile devices, see Fix Construct 3 Sprite Flickering on Mobile Devices.
Profile before you optimize. The bottleneck is almost never where you think it is. A project running at 30 FPS might have a single event group consuming 40% of the CPU budget, and disabling it could double your frame rate instantly.