vfx-lightning is a skill from the Summer skill library: a guide your AI agent reads when the task matches, so it builds the good version on the first try. Lightning bolt effect: a jagged procedural path with a glow shader, end sparks and screen shake.
When your agent uses it
- Authoring a lightning bolt effect — chain lightning, electric attack, tesla coil, shock spell, energy beam
- A spell, weapon, or hazard needs a jagged electric arc with glow, endpoint sparks, and camera shake
Install
summer setup installs every skill for your agent. To add only this one:
claude-code for your agent, such as codex or cursor. See Skills for every option.
The skill
A lightning bolt is a noisy line from A to B, drawn for ~150 ms with a glow shader, sparks at both endpoints, and camera shake on cast. The recipe builds the jagged path in GDScript (midpoint displacement on a polyline), feeds the verts into anImmediateMesh, applies a ShaderMaterial for additive bloom + flicker, spawns vfx-hit-spark particles at each endpoint, and calls CameraShake.add_trauma(0.6) for the impact. Used for shock spells, tesla coils, weapon discharges, electric enemies.
When to use
- “Cast a lightning bolt from the wizard’s hand to the enemy.”
- “Chain lightning from one enemy to the next.” (call
cast_lightningper segment) - “Tesla coil firing at the player every 2 seconds.”
- “Energy weapon laser.” (use the
plasma-laservariant) - “Lightning strike from the sky.”
- The user wants a short, dramatic energy connection between two points.
When NOT to use
- The user wants a continuous beam that holds (e.g., laser sniper holding the trigger) — use the same shader on a
MeshInstance3Dcylinder with continuous emission, not theImmediateMeshone-shot pattern. (See theplasma-laservariant.) - The user wants ambient electricity on an object (Frankenstein arcs) — use a custom shader on the mesh with screen-space arcs; this recipe is for two-point bolts.
- The user wants a 2D lightning effect on UI —
canvas_itemshader, not this. - The user wants a tracer line for a bullet — use a simple
ImmediateMeshwithout noise displacement; skip this recipe. The current Summer build has noLine3D(ClassDB.class_exists("Line3D")isfalse);Line2Dis 2D-only.
Recipe
1. Files to create
2. Shader code
addons/vfx/lightning/lightning.gdshader:
3. GDScript controller
addons/vfx/lightning/lightning_caster.gd:
4. Node tree
LightningCaster as an autoload (/root/Lightning) so any system can call Lightning.cast_lightning(from, to) from anywhere.
5. Wire it in (MCP calls)
Place oneLightningCaster per “world” (or autoload it), then call from gameplay code.
Write the two files, then place the caster with ONE summer_run_script call
with an explicit scenePath — one transactional run, and several casters or
anything computed is a loop in the same script:
preloads its own shader, so a shader that failed to write
takes the whole script down at parse time:
6. Parameters to tune
Cookbook — named variants
thunderbolt (default)
Cool blue-white, dramatic shake, sky-to-ground.chain-lightning-spell
Tighter, chains between targets.tesla-arc
Short, fast, lots of forks.plasma-laser (held beam variant)
Override the script to NOT free afterLIFETIME — use a MeshInstance3D with a stretched cylinder mesh updated each frame between two transforms; same shader.
Anti-patterns
- Reaching for a
Line3D. The current Summer build has no such class (ClassDB.class_exists("Line3D")isfalse);Line2Dis 2D-only. Use anImmediateMeshtriangle strip with the billboard vertex shader. - Animating the path frame-by-frame. A bolt is one shape, on screen for 150 ms. You don’t see the inside detail moving. One generation per cast.
- Forgetting
blend_add. A bolt that doesn’t bloom looks like a curved metal stick. - No camera shake. Lightning without screen shake feels weightless. Always call
add_traumaon the/root/CameraShakeautoload. - Gating the shake on
Engine.has_singleton("CameraShake"). That is alwaysfalsefor an autoload (measured) — the shake would never fire. Autoloads are nodes at/root/<Name>; look them up withget_node_or_null. - Spawning lightning every frame for a “continuous” beam. Use the
plasma-laservariant pattern instead — one mesh, updated per-frame transform, never re-instantiated. SEGMENTStoo low (< 6). Bolt looks like a zigzag triangle. Default 18 is right.DISPLACEMENTproportional to total length without falloff. Endpoints drift away fromfrom/to. The included code usessin(t * PI)falloff so endpoints meet cleanly.
Performance notes
- One bolt at
SEGMENTS = 18: 19 path points × 2 verts = 38 verts for the trunk, plus 2 × (SEGMENTS / 3+ 1) = 14 verts per forked branch. One draw call, freed after 150 ms. Effectively free. - Chain lightning with 5 segments stays in the same negligible band — that is an order-of-magnitude expectation, not a measurement.
- Endpoint sparks (instantiated
vfx-hit-sparkscenes) are the bigger cost — 64 particles × 2 = 128 particles per cast. Throttle for storms (see edge cases). - The shader’s flicker uses
floor(TIME * flicker_rate), so it ages withTIMEnot particle time — flicker is consistent across all bolts in flight.
Edge cases
- Lightning storm (50 strikes/sec). Disable endpoint sparks for ambient strikes. Pool the
LightningCastermesh nodes instead of allocating per cast. - Bolt passes through a wall. Visual only — the bolt shader doesn’t depth test. If the user wants the bolt to be occluded by walls, set
depth_draw_opaqueand let the depth buffer cull it (loses the additive bloom in front of geometry). - Bolt origin and target at the same point. Falloff goes to zero;
_generate_pathreturns a degenerate strip. Add a guard: if(to - from).length() < 0.01, skip. - Underwater bolt. Tint blue-green, drop
emission_boostto 5.0; underwater bloom is muted. - First-person caster (player’s hand). The bolt starts inside the player’s view and reads as a flash. Use the
plasma-laservariant or shortenLIFETIMEto 0.08. - No
CameraShakeautoload registered.get_node_or_null("/root/CameraShake")returns null and theif cscheck skips the call; the bolt fires without shake. Suggest_building-blocks/trauma-shake-snippet.mdto wire it.
Fallback (no MCP)
Section 5 is fully automatable —summer_write_file writes the shader and the
caster, summer_add_node + summer_set_prop + summer_save_scene place it, and
summer_project_setting registers the autoload. Do not hand these steps to the user
when the MCP tools are available.
Without the MCP connection there is no engine to drive, so the user does it
manually in Summer Engine:
- Create
addons/vfx/lightning/and write the three files above. - Add a
Node3D, attachlightning_caster.gd. Optionally autoload asLightning. - From any spell script, call
Lightning.cast_lightning(from_pos, to_pos). - Wire the
CameraShakeautoload from_building-blocks/trauma-shake-snippet.mdfor the punch. - Check the Errors dock —
lightning_caster.gdpreloads the shader, so a broken shader path fails the script too.
Handoff
After firing this recipe, suggest:vfx-hit-spark— automatically called at endpoints; tune the spark color to matchbolt_color.vfx-magic-glow— for the wizard’s hand glow during charge-up before the cast.vfx-muzzle-flash— for the brief bright flash at the casting hand on release frame.game-feel—CameraShake.add_traumais already called; pair with hit-stop on the target for impact emphasis.sound-effect— generateelectric crack thunder, sharp impact, rumble tail, 800msand play on cast.
See also
_building-blocks/trauma-shake-snippet.md—CameraShakeautoload (REQUIRED for the shake call)_building-blocks/additive-billboard-particles.md— for the endpoint sparksvfx-hit-spark— endpoint companionvfx-magic-glow— pre-cast charge-upvfx-muzzle-flash— for the cast-release flashgame-feel— full game-feel pairing
From a shell
No Summer MCP tools in this session? Run each one as a command; whensummer --version is missing or below 4.0, use npx -y summer-engine@latest in place of summer. Fields are flags in kebab-case (--scene-path res://main.tscn) or one --input '<json>', and summer ops describe <op> prints the exact input. The summer-cli skill has the rest.
Files in this skill
Related skills
scene-scripting: Build scenes, levels, HUDs and wiring with one editor GDScript (summer_run_script) instead of call chains.sound-effect: Make sound effects (swings, hits, whooshes, reloads, UI): three versions to pick from, then wire them.game-feel: Add juice: hit-flash, trauma camera shake and audio ducking, one hit fires all three.vfx-hit-spark: Hit-spark effect: a one-shot burst of additive particles oriented to the surface, fired on impact.vfx-magic-glow: Magic-glow effect: a pulsing OmniLight3D, drifting additive motes and an optional emission shader.vfx-muzzle-flash: Muzzle-flash effect: a one-shot ~80 ms burst at a gun barrel, from particles or a star-burst quad.
Source:
library/skills/vfx-lightning/SKILL.md, rendered from summer-engine@4.3.0 on npm.
