From summer
Creates a magical glow effect for 3D objects using a pulsing OmniLight3D, drifting additive motes, and optional emission shader. Useful for enchanted items, soul gems, runes, summoning circles, wisps, and quest objects.
How this skill is triggered — by the user, by Claude, or both
Slash command
/summer:magic-glow**/*.tscn**/*.gd**/*.gdshaderaddons/vfx/**This skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Three layered cues that together read as "magical": a pulsing `OmniLight3D` (sin-wave energy), drifting additive billboard motes orbiting the source, and optional emission on the source mesh itself. Used for: enchanted items, soul gems, runes, summoning circles, wisps, fairies, magical waypoints. The recipe writes a single controller that builds and animates all three layers from one node.
Three layered cues that together read as "magical": a pulsing OmniLight3D (sin-wave energy), drifting additive billboard motes orbiting the source, and optional emission on the source mesh itself. Used for: enchanted items, soul gems, runes, summoning circles, wisps, fairies, magical waypoints. The recipe writes a single controller that builds and animates all three layers from one node.
lightning or muzzle-flash (charge-up beam).fire (or magic-fire variant of fire).lightning with the plasma-laser variant.WorldEnvironment.glow_*, not a per-object recipe.canvas_item shader with a radial gradient, not this 3D recipe.addons/vfx/magic-glow/magic_glow.gd
addons/vfx/magic-glow/magic_glow.tscn
No custom shader — uses canonical additive billboard motes (see _building-blocks/additive-billboard-particles.md) plus an OmniLight3D. Optional: pair with the source mesh's existing material's emission.
addons/vfx/magic-glow/magic_glow.gd:
@tool
class_name MagicGlow
extends Node3D
## Builds: OmniLight3D (pulsing) + GPUParticles3D motes (drifting orbit) + optional mesh emission tween.
## Add as a child of any glowing object.
@export_group("Color")
@export var glow_color: Color = Color(0.55, 0.85, 1.0)
@export_range(0.0, 12.0) var light_energy_base: float = 1.4
@export_range(0.0, 12.0) var light_energy_amplitude: float = 0.8
@export_group("Pulse")
@export_range(0.05, 4.0) var pulse_hz: float = 0.7
@export var pulse_easing: Curve ## leave null for sine; assign for custom
@export_group("Light")
@export_range(0.5, 30.0) var light_range: float = 4.0
@export var light_shadow_enabled: bool = false
@export_group("Motes")
@export_range(0, 256) var mote_count: int = 24
@export_range(0.05, 4.0) var mote_radius: float = 0.6
@export_range(0.5, 8.0) var mote_lifetime: float = 2.0
@export_range(0.01, 0.30) var mote_size: float = 0.06
@export_range(0.0, 8.0) var mote_emission_boost: float = 4.0
@export var mote_drift_up: float = 0.3 ## meters/sec mean upward drift
@export_group("Source mesh emission (optional)")
@export var pulse_source_mesh: NodePath ## point at a MeshInstance3D to tween its emission_energy_multiplier
@export_range(0.0, 12.0) var source_emission_base: float = 1.0
@export_range(0.0, 12.0) var source_emission_amplitude: float = 1.5
var _light: OmniLight3D
var _motes: GPUParticles3D
var _source_mesh: MeshInstance3D
var _source_mat: BaseMaterial3D
var _t: float = 0.0
func _ready() -> void:
_build_light()
_build_motes()
_resolve_source_mesh()
set_process(true)
func _build_light() -> void:
_light = OmniLight3D.new()
_light.light_color = glow_color
_light.light_energy = light_energy_base
_light.omni_range = light_range
_light.shadow_enabled = light_shadow_enabled
add_child(_light)
func _build_motes() -> void:
if mote_count <= 0:
return
_motes = GPUParticles3D.new()
_motes.amount = mote_count
_motes.lifetime = mote_lifetime
_motes.one_shot = false
_motes.explosiveness = 0.0
_motes.local_coords = true
var pm := ParticleProcessMaterial.new()
pm.emission_shape = ParticleProcessMaterial.EMISSION_SHAPE_SPHERE
pm.emission_sphere_radius = mote_radius
pm.direction = Vector3.UP
pm.spread = 180.0
pm.initial_velocity_min = 0.05
pm.initial_velocity_max = 0.20
pm.gravity = Vector3(0, mote_drift_up * 0.5, 0)
pm.scale_min = mote_size * 0.7
pm.scale_max = mote_size * 1.3
pm.color = glow_color
pm.damping_min = 0.5
pm.damping_max = 1.2
_motes.process_material = pm
var mesh := QuadMesh.new()
mesh.size = Vector2.ONE
var bm := BaseMaterial3D.new()
bm.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
bm.blend_mode = BaseMaterial3D.BLEND_MODE_ADD
bm.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
bm.billboard_mode = BaseMaterial3D.BILLBOARD_PARTICLES
bm.albedo_color = glow_color
bm.emission_enabled = true
bm.emission = glow_color
bm.emission_energy_multiplier = mote_emission_boost
mesh.material = bm
_motes.draw_pass_1 = mesh
add_child(_motes)
func _resolve_source_mesh() -> void:
if pulse_source_mesh.is_empty(): return
var n := get_node_or_null(pulse_source_mesh)
if n is MeshInstance3D:
_source_mesh = n
var m := _source_mesh.get_active_material(0)
if m is BaseMaterial3D:
_source_mat = m
_source_mat.emission_enabled = true
if _source_mat.emission == Color.BLACK:
_source_mat.emission = glow_color
func _process(delta: float) -> void:
_t += delta
var phase: float = (sin(_t * TAU * pulse_hz) + 1.0) * 0.5 # 0..1
if pulse_easing:
phase = pulse_easing.sample(phase)
if _light:
_light.light_energy = light_energy_base + light_energy_amplitude * phase
if _source_mat:
_source_mat.emission_energy_multiplier = source_emission_base + source_emission_amplitude * phase
func set_intensity(scale: float) -> void:
## Scale all visible cues 0..1+ (e.g., charge-up: tween from 0 to 1 over a second).
light_energy_base *= scale
light_energy_amplitude *= scale
if _motes:
_motes.amount_ratio = scale
func extinguish(fade_seconds: float = 0.6) -> void:
var t := create_tween().set_parallel(true)
t.tween_property(_light, "light_energy", 0.0, fade_seconds)
if _motes:
t.tween_property(_motes, "amount_ratio", 0.0, fade_seconds)
t.chain().tween_callback(queue_free)
Node3D ("MagicGlow") [script: magic_glow.gd]
├── OmniLight3D (created at runtime, pulses)
└── GPUParticles3D (created at runtime, drifting motes)
For an enchanted sword:
summer_add_node(parent="./Player/Hand/Sword", type="Node3D", name="Glow")
summer_set_prop(path="./Player/Hand/Sword/Glow", property="script", value="res://addons/vfx/magic-glow/magic_glow.gd")
summer_set_prop(path="./Player/Hand/Sword/Glow", property="glow_color", value="Color(0.55, 0.85, 1.0)")
summer_set_prop(path="./Player/Hand/Sword/Glow", property="light_energy_base", value=1.2)
summer_set_prop(path="./Player/Hand/Sword/Glow", property="light_energy_amplitude", value=0.6)
summer_set_prop(path="./Player/Hand/Sword/Glow", property="mote_count", value=16)
summer_set_prop(path="./Player/Hand/Sword/Glow", property="mote_radius", value=0.20)
summer_set_prop(path="./Player/Hand/Sword/Glow", property="pulse_source_mesh", value="../Blade")
summer_save_scene
For a soul gem on a pedestal:
summer_add_node(parent="./World/Pedestal/SoulGem", type="Node3D", name="Glow")
summer_set_prop(path="./World/Pedestal/SoulGem/Glow", property="glow_color", value="Color(0.85, 0.45, 1.0)")
summer_set_prop(path="./World/Pedestal/SoulGem/Glow", property="pulse_hz", value=0.4)
summer_set_prop(path="./World/Pedestal/SoulGem/Glow", property="mote_count", value=32)
summer_set_prop(path="./World/Pedestal/SoulGem/Glow", property="mote_radius", value=0.30)
For a charge-up before casting:
# Spawn at the wizard's hand:
var glow: MagicGlow = preload("res://addons/vfx/magic-glow/magic_glow.tscn").instantiate()
$Wizard/HandTip.add_child(glow)
glow.set_intensity(0.0)
var t := create_tween()
t.tween_method(glow.set_intensity, 0.0, 1.0, 0.8)
t.tween_callback(func() -> void:
Lightning.cast_lightning($Wizard/HandTip.global_position, target.global_position)
glow.extinguish(0.3))
| Parameter | Range | Effect |
|---|---|---|
glow_color | Color | controls light, motes, and (if linked) source emission |
light_energy_base | 0.0–12.0 | resting light brightness |
light_energy_amplitude | 0.0–12.0 | pulse swing on top of base |
pulse_hz | 0.05–4.0 | beats per second; <1 for slow breathing, >2 for buzzing |
light_range | 0.5–30.0 m | reach of the light cast |
mote_count | 0–256 | number of motes orbiting (0 = light only) |
mote_radius | 0.05–4.0 m | spawn sphere radius around the source |
mote_lifetime | 0.5–8.0 s | how long each mote drifts |
mote_size | 0.01–0.30 m | per-mote billboard size |
mote_emission_boost | 0.0–8.0 | bloom strength of motes |
mote_drift_up | -1.0–3.0 | mean upward drift (negative = sink) |
pulse_source_mesh | NodePath | optional: pulse a mesh's emission with the same phase |
Cool blue, gentle pulse, few tight motes.
glow_color = Color(0.55, 0.85, 1.0)
light_energy_base = 1.2
light_energy_amplitude = 0.5
pulse_hz = 0.7
light_range = 3.0
mote_count = 16
mote_radius = 0.15
mote_size = 0.04
pulse_source_mesh = "../Blade" # blade emission pulses too
Deeper purple, slow heartbeat pulse, many motes.
glow_color = Color(0.75, 0.30, 1.0)
light_energy_base = 1.0
light_energy_amplitude = 0.8
pulse_hz = 0.4
light_range = 4.0
mote_count = 32
mote_radius = 0.25
mote_size = 0.05
mote_drift_up = 0.4
Strong, broad, many motes drifting upward.
glow_color = Color(0.45, 0.80, 1.0)
light_energy_base = 0.8
light_energy_amplitude = 1.6
pulse_hz = 0.3
light_range = 8.0
mote_count = 96
mote_radius = 1.5
mote_size = 0.08
mote_drift_up = 1.0
mote_lifetime = 3.0
Tiny floating orb. Attach to a PathFollow3D for the wandering motion.
glow_color = Color(0.85, 1.0, 0.65)
light_energy_base = 0.6
light_energy_amplitude = 0.3
pulse_hz = 1.2
light_range = 2.5
mote_count = 8
mote_radius = 0.10
mote_size = 0.03
mote_drift_up = 0.0
Starts at 0 intensity, ramped via set_intensity. Set initial values for fully-charged state.
glow_color = Color(0.55, 0.75, 1.0)
light_energy_base = 0.0 # script tweens this up
light_energy_amplitude = 2.0
pulse_hz = 2.5 # buzzing
mote_count = 24
mote_radius = 0.20
mote_drift_up = 0.5
light_shadow_enabled = true on every magical item. A player carrying a glowing sword adds a fast-moving shadow caster; expensive. Default off.light_range to the item's importance.BLEND_MODE_MIX. Motes look like opaque dots, not glowing dust. Always BLEND_MODE_ADD.local_coords = false) for an item the player carries. Motes trail behind as the player walks. Default local_coords = true._resolve_source_mesh enables it.pulse_hz too high. Above 3 Hz it strobes — accessibility issue (photosensitivity). Default 0.7 is calm.OmniLight3Ds before you should consider LightOccluder3D culling. Motes scale linearly to ~512 across all glows.mote_count to 8, light_range × 0.5, light_shadow_enabled = false always._process, scale _motes.amount_ratio and _light.light_energy by clamp(1.5 - dist_to_camera / 20.0, 0.10, 1.0). Past 20 m, drop motes entirely; keep light.local_coords = true (default) and parent under the NPC. The light moves with the character.pulse_hz and slightly offset phases → richer feel. Use pulse_easing curve to sync if you want them to beat together.light_range 50% (water absorbs light); tint slightly green.pulse_hz from your audio system's BPM, or replace _process with an AnimationPlayer track timed to a music event.VFX is code, no MCP required:
addons/vfx/magic-glow/ with the two files above.Node3D child to the glowing object, attach magic_glow.gd. Save as magic_glow.tscn if you want a scene to instantiate.After firing this recipe, suggest:
summer:visual-effects/recipes/lightning — perfect pre-cast charge-up. Use the charge-up variant and ramp set_intensity(0→1) then call Lightning.cast_lightning.summer:visual-effects/recipes/dissolve — pair summon-arrival (materialize_object) with the summoning-circle variant.summer:visual-effects/recipes/fire (the magic-fire variant) — for items that should glow AND flame.summer:visual-effects/game-feel — slight time-dilation when picking up a pulsing quest item adds weight.summer:audio/sound-effect — generate magical hum, ethereal shimmer, 2s loop and play on the same anchor._building-blocks/additive-billboard-particles.md — canonical additive material settings (this recipe uses them for motes)summer:visual-effects/recipes/lightning — pre-cast charge-up partnersummer:visual-effects/recipes/dissolve — for summon arrival pairingsummer:visual-effects/recipes/fire — for the magic-fire variantsummer:gdscript-patterns — for the controller idioms (signals, exports, @tool)npx claudepluginhub summerengine/summer-engine-agent --plugin summerProvides Godot 4.3+ 3D essentials: coordinate system, core nodes (Node3D, MeshInstance3D, lights, WorldEnvironment, Decal), materials (StandardMaterial3D, ORMMaterial3D, ShaderMaterial), lighting, environment, fog, LOD, occlusion culling, decals. GDScript and C# examples.
Writes Godot 4.x shaders (.gdshader files) for 2D canvas_item effects, 3D spatial materials, particles, sky, fog, and post-processing. Covers uniforms, hints, structure, and built-in variables.