Help us improve
Share bugs, ideas, or general feedback.
From summer
Profiles running Godot games using Summer MCP diagnostics, identifies hotspots in rendering/physics/scripting, and proposes targeted fixes with before/after metric tracking.
npx claudepluginhub summerengine/summer-engine-agent --plugin summerHow this skill is triggered — by the user, by Claude, or both
Slash command
/summer:tune-performance**/*.gd**/*.tscn**/*.tresproject.godotThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Performance tuning without measurement is gambling. This skill enforces a measure-first loop: read diagnostics, identify the dominant cost (rendering / physics / scripting / startup), drill into the specific pattern, propose one fix, verify the metric moved. No shotgun optimization.
Optimizes Godot games via built-in profiler, performance monitors, and fixes for bottlenecks like excessive _process calls, inefficient node lookups, and get_tree usage. Use for FPS drops or performance analysis.
Profiles Godot runtime, scene, rendering, and resource behavior from representative builds. Use when performance targets are at risk or scene complexity rises.
Game optimization, performance profiling, multi-platform support, and frame rate optimization for smooth gameplay experiences.
Share bugs, ideas, or general feedback.
Performance tuning without measurement is gambling. This skill enforces a measure-first loop: read diagnostics, identify the dominant cost (rendering / physics / scripting / startup), drill into the specific pattern, propose one fix, verify the metric moved. No shotgun optimization.
Core principle: the engine's diagnostics tell you which subsystem is bleeding. Don't optimize a different subsystem.
What's slow? Pick the closest: framerate drops in scene X / startup is long / freezes for a moment / runs fine on my machine but bad on hardware Y.
Wait. The answer narrows the fix domain by 5x:
| Symptom | Likely subsystem |
|---|---|
| Framerate drops as more enemies spawn | Scripting (_process per-instance) or physics |
| Framerate is fine standing still, drops looking at level X | Rendering (overdraw, draw calls, lights) |
| Stutter every N seconds | GC pause, autoload loop, or async load |
| Long startup | Asset import, autoload _ready work, shader compilation |
| Runs fine on dev machine, bad on user machine | Resolution, GPU features (compute, GI), shadow quality |
Don't guess. Read the engine's actual numbers.
Preferred (Summer MCP):
summer_clear_console
summer_play
# user reproduces the slowdown for 5–10 seconds
summer_get_diagnostics
summer_get_console # check for warnings (e.g. "shader compilation hot-path")
summer_stop
summer_get_diagnostics returns the aggregate metrics. Note (write down before fixing anything):
Fallback (no MCP): ask the user to enable Godot's built-in monitor (Debug → Monitor or in-game with Performance.get_monitor) and paste numbers. At minimum: FPS, frame time, draw calls, physics active objects.
The biggest number wins your attention. Don't optimize the second-biggest — that's a 5% win when 30% is sitting next to it.
Decision tree:
Common Godot 4.5 rendering costs, ordered by frequency:
| Pattern | Symptom | Fix |
|---|---|---|
| Too many MeshInstance3D | Draw calls > 1000 | MultiMeshInstance3D for repeated meshes (foliage, debris). 5000 trees = 1 draw call. |
| Overdraw (transparent stacked) | Render time spikes when looking at particles/glass/UI | Reduce particle amount, disable transparent on opaque materials, check Compositor settings |
| Real-time lights > 4 visible | Frame time doubles in well-lit rooms | Bake static lighting (LightmapGI), make distant lights shadow_enabled = false |
| Shadow map size too high | Render time spikes near light sources | DirectionalLight3D directional_shadow_max_distance lower, OmniLight3D shadow_bias up, shadow size 2048 → 1024 |
| No occlusion culling | Far rooms drawn through walls | OccluderInstance3D + bake. Painful but high-impact for indoor scenes. |
| Missing LOD on complex meshes | Drop in framerate when far away | MeshInstance3D visibility_range_begin + manual LOD swaps |
To inspect a suspect node:
summer_inspect_node "./World/Enemy"
summer_inspect_resource "./World/Enemy" # for mesh/material/shape details
Top patterns:
| Pattern | Fix |
|---|---|
| 100+ active RigidBody3D | Sleep them when idle (can_sleep = true), or convert to Area3D where collision response isn't needed |
| Concave collision shapes on dynamic bodies | Convert to ConvexPolygonShape3D or compound primitives (sphere/capsule/box). Concave costs 5–20x more. |
| Physics tick at 120 Hz default | Lower physics/common/physics_ticks_per_second to 60 if visual smoothness allows |
Per-frame raycast in _physics_process | Cache the result, raycast every N frames, or use ShapeCast3D |
| Hundreds of overlap queries | Use Area3D with monitoring = true only on the bodies that need to detect — turn it off on environment |
Per-frame work in _process is the #1 GDScript cost. Read the script, look for:
# BAD — runs every frame
func _process(delta: float) -> void:
var enemies = get_tree().get_nodes_in_group("enemies") # tree walk!
for enemy in enemies:
var dist = global_position.distance_to(enemy.global_position)
if dist < 10.0:
...
Fixes:
_ready() if the group composition is stable._on_timer_timeout), not 60 Hz.distance_to with distance_squared_to in comparisons (no sqrt).@tool-able C# script or GDExtension if it's truly per-frame.If FPS is good but frame time spikes:
RenderingServer.shader_compile_from_code or by warming up the scene off-screen.var x := [] inside _process allocates each frame. Pre-allocate.ResourceLoader.load_threaded_request is finished before referencing.Check summer_get_console for shader-compile warnings.
summer_clear_console
summer_play
# stop after first scene loads
summer_stop
summer_get_console
Look for:
_ready() work (move to call_deferred or background thread)..import files in source control).preload() of huge scenes at script-parse time — switch to load() on demand.Don't bundle five fixes. Pick the highest-leverage one.
The dominant cost is rendering at 14.2 ms / frame (85% of budget). The biggest source is 240 individual MeshInstance3D for foliage. Replacing them with one MultiMeshInstance3D should drop render time to ~3 ms (estimate: 5–7 ms). May I refactor
./World/Foliageto use MultiMeshInstance3D?
Format the proposal as: (a) what the dominant cost is, (b) the specific source, (c) the fix, (d) the expected metric movement, (e) the ask.
After the user approves:
# apply the fix (Edit / summer_add_node / summer_set_prop / etc)
summer_save_scene
summer_clear_console
summer_play
# user reproduces the same scenario
summer_get_diagnostics
summer_stop
Compare: was the metric movement at least 50% of what was promised? If yes, ship it. If no, the hypothesis was wrong — go back to step 3 with the new numbers.
Before: 14.2 ms render / 22 fps. After: 4.1 ms render / 58 fps. Shipped.
| Don't | Do | Why |
|---|---|---|
| Optimize without baseline metrics | Always summer_get_diagnostics before and after | "Felt faster" is not a measurement |
| Fix the second-biggest cost | Fix the biggest first, re-measure, re-prioritize | Amdahl's law — the small win evaporates next to the big one |
| Bundle 5 fixes into one PR | One fix at a time, verify each | Bundles hide regressions. Hard to attribute the win or the loss. |
_process for everything | 10 Hz timer for AI / distance checks / range polls | 60 Hz AI is wasted CPU |
distance_to in hot loop | distance_squared_to in comparisons | sqrt is expensive at scale |
| 200 RigidBody3D for debris | MultiMeshInstance3D + manual physics, or bake | Each rigid body = solver work every tick |
| Real-time GI everywhere | Bake static lighting; real-time only for movable lights | Real-time GI is a per-frame budget killer |
| Shadow map at 4096 on every light | 4096 for sun, 1024 or off for fillers | Shadow map writes are hidden render passes |
| Concave shape on dynamic body | Compound primitives (sphere + box + capsule) | Concave shape physics is 5–20x slower |
get_nodes_in_group every frame | Cache once in _ready() | Tree walks aren't free |
_physics_process.This skill makes scene/resource/code changes. Always ask before each fix is applied. See references/collaborative-protocol.md.
No template — this is a workflow. Performance tuning is project-specific by definition.
references/mcp-tools-reference.md — full MCP tool listreferences/godot-version.md — Godot 4.5 renderer notes (Compositor, RenderSceneBuffers churn)references/collaborative-protocol.md — "May I write" patternreferences/gd-style.md — GDScript conventions (avoid bare types, use :=)performance/profiling-godot/SKILL.md — deeper Godot profiler usageperformance/draw-call-batching/SKILL.md — MultiMeshInstance3D patternsperformance/lod-and-culling/SKILL.md — LOD setupdebugging/debug/SKILL.md — bug triage (related but different)rendering-and-lighting/baked-vs-realtime-lighting/SKILL.md — bake decisions