Design the read order
A magic circle should not be one glowing texture. Decide what the player sees first, second, and third. Usually that means a strong outer ring, a slower inner symbol, then small decorative lines.
UE5 / Recipe / Stylized VFX
A readable floor-magic material for spell circles, telegraphs, teleport pads, and arena warnings. The recipe is about hierarchy: outer ring, inner symbol, pulse timing, and emission should each have a job.
A magic circle should not be one glowing texture. Decide what the player sees first, second, and third. Usually that means a strong outer ring, a slower inner symbol, then small decorative lines.
01
The useful read is the ring hierarchy: outer boundary first, inner symbol second, pulse motion last.
02
03
Treat the rune as layers: boundary, symbols, radial pulse, and optional noise. Each layer should be previewable alone. If a layer does not improve readability or mood, remove it.
Thin glyphs look great close-up and disappear from a combat camera. Build a bold version first, then add delicate line art only if it survives the intended distance.
04
Use distance from UV center to make a controllable ring. This keeps radius and thickness adjustable without repainting the texture.
Use a texture or atlas for the actual glyph shapes. Keep this separate from the procedural ring so symbols can be swapped per spell family.
A radial pulse can expand outward, contract inward, or rotate. Use phase offsets so the ring, symbol, and pulse do not all blink at the same time.
Outer ring usually gets the strongest readability value. Inner symbols get medium emission. Decorative noise stays low.
If the circle intersects uneven ground, depth fade can soften harsh clipping. Keep it subtle so the ring boundary remains clear.
05
06
float2 centeredUV = uv * 2.0 - 1.0;
float radius = length(centeredUV);
float angle = atan2(centeredUV.y, centeredUV.x);
float ringDelta = abs(radius - ringCenter);
float outerRing = 1.0 - smoothstep(ringRadius, ringRadius + ringSoftness, ringDelta);
float runeMask = Texture2DSample(runeTex, runeTexSampler, uv).r;
float pulse = frac(radius * pulseDensity - time * pulseSpeed + pulsePhase);
float pulseBand = 1.0 - smoothstep(0.0, pulseSoftness, pulse);
float mask = saturate(outerRing * ringWeight + runeMask * runeWeight + pulseBand * pulseWeight);
float3 color = ringColor * outerRing + runeColor * runeMask + pulseColor * pulseBand;
return float4(color * mask * emissionStrength, mask);
07
For combat telegraphs, the player needs to know the boundary instantly. If the rune art competes with the ring edge, reduce it.
Decals sit nicely on level geometry but may have sorting or projection constraints. Planes are simple and controllable but can float or intersect badly.
Consider Ready, Charging, Warning, and Expired values. A circle often communicates timing, not only style.
08