Faking depth
This is an exploration of how to give browser text the illusion of real three-dimensional depth — a problem that came up on a real client project where the design called for heavy display type with a distinct extruded shadow.
The type should stay selectable, scalable, and accessible, which rules out reaching for a static image or a full WebGL scene. Then again, the possibilities on the web can get pretty creative if you are willing to experiment with client-side rendering techniques, so we set out to find the best way to pull off this effect with live text. What follows is a walkthrough of each approach we tried, what we learned, and when you might actually reach for it.
Baseline: the font alone
It’s worth starting without any effect. The font — a wide, heavy variable display face — does a lot of work on its own. Add in a bit of stroke with a high contrast colour and you can get a hint of depth without any shadows at all.
Everything in the block below is basic HTML. No canvas, no SVG wrapper. It reflows, honors your styling, scales with the viewport, and works with screen readers and search engines out of the box. This is the baseline all the other approaches are measured against.
Since 1961
Bold type
Web rendering
Approach 1: layered text-shadow
The most obvious tool is the CSS text-shadow property. A single declaration gives you one shadow, but the spec allows any number of comma-separated values. By repeating a shadow at incrementally larger offsets in the same direction, you build up a convincing extrusion.
The key lever is offset progression. Linear steps produce a uniform block face. An eased curve — quadratic or cubic — makes the shadow taper toward the vanishing edge, implying a light source. Interpolating opacity across layers simulates ambient occlusion gathering in the deeper recesses.
Since 1961
Bold type
Web rendering
Pros: Entirely declarative. The element stays a normal DOM node — selectable, responsive, participates in layout like any heading. Zero JavaScript at render time. Performance is good since the shadow stack is composited inside the browser’s own text rendering pipeline.
Cons: Each shadow is the same glyph shape offset and recolored, but without the stroke outline that the foreground text carries. At certain depth and angle combinations, adjacent shadow copies overlap, creating a faint double-edge on the extruded face instead of a solid fill. It rarely matters in practice but shows at large sizes with shallow angles.
Verdict: easy to implement, scalable, accessible. If you can live without the shadow matching the stroke exactly, this is the right starting point for most projects.
Approach 2: layered SVG <text>
SVG gives you <text> elements with explicit coordinate control. The idea is to render the same string multiple times at increasing offsets, each copy in the shadow colour, then place the real foreground text on top. Unlike CSS shadows, each layer is a genuine separate draw call, so the fills are geometrically correct — no overlap blending artefacts.
Pros: Each layer has a precisely defined outline. You can stroke every shadow copy independently, giving a crisper edge on the extruded face. SVG coordinates allow sub-pixel accuracy regardless of font size.
Cons: The SVG replaces the original heading entirely and needs ARIA additions to stay accessible. Every layer duplicates the text string in the DOM, ballooning markup size. The SVG needs a viewBox, which means either hard-coding a size or measuring text at runtime — both are awkward in a responsive layout.
Verdict: geometrically correct extrusion at the cost of layout flexibility, accessibility, and manual sizing work. Not recommended for general use, but viable for a fixed-size hero or title card.
Approach 3: SVG blend steps
A variation of approach 2, modelled on how Illustrator’s Blend tool works. Rather than stepping each layer by a fixed pixel offset from the origin, you define a total extrusion distance and a step count, then evenly interpolate all intermediate copies between the front position and the back. The resulting geometry is similar to approach 2 but with different elemtn sizing. The biggest difference is the mental model that maps directly to how a designer specifies depth in a vector application.
Pros: Cleaner API — specify total extrusion distance and step count rather than per-layer offsets. Easier to reason about for a designer. Produces the same visual quality as approach 2.
Cons: All the same layout and accessibility drawbacks as approach 2, plus the SVG centers its content within the viewBox to make room for the extrusion, which introduces additional sizing complexity. The blend abstraction adds no real browser savings over a simple loop.
Verdict: same tradeoffs as approach 2 with a marginally nicer API that only helps if your team thinks natively in Illustrator terms. Still not recommended for general use. The viewBox calculation needs solving before it becomes viable in a responsive context.
Approach 4: SVG feOffset filter chain
SVG filters are a compositing pipeline that operates on whatever element they are applied to. Instead of rendering many copies of the text, you build a filter that takes the source alpha mask, offsets it n times, floods each offset with the shadow colour, merges all the results, and composites the original text back on top. The entire effect lives in a <filter> element and is applied to an ordinary HTML heading via filter: url(#id).
Since 1961
Bold type
Web rendering
Pros: The source element stays as a genuine HTML heading — no viewBox, no hardcoded font sizes, no ARIA workarounds. The filter scales automatically with the element. Fills are solid and mostly geometrically correct. This is the approach that most closely matched the Illustrator mockup the design was originally produced in.
Cons: Building the filter graph generates an SVG <defs> block per unique set of parameters. In practice you may only need one filter definition per visual style — not one per heading instance. Performance is good for display-sized type but can degrade on mobile at large layer counts but can be optimized by tweaking the layer count based on the font size and extrusion depth to avoid wasteful overdraw.
Verdict: The best balance of visual quality, accessibility, and layout compatibility. The filter graph is a bit verbose and adds extra elements to your DOM but can be optimized to scale better with multiple instances. If you need a solid, geometrically correct extrusion, reach for this approach.
Which approach to use
For most cases layered CSS shadows win on simplicity, performance, and layout compatibility with zero overhead and full DOM participation. Reach for the SVG Filter method when you need more geometrically correct, solid shadow faces and are comfortable managing a small <defs> fragment. Avoid the other SVG <text> approaches for anything that needs to respond fluidly to its container — the fixed viewBox will fight you. And if the text is purely decorative and the brief calls for something theatrical, Three.js is hard to beat.
The controls panel (press D) exposes every parameter live: layer count, depth, angle, progression curve, opacity range, and colors. The approach switcher above the article reflects the same state.
Bonus demos
Bonus 1: Text on a path
A quick detour into SVG <textPath>. Nothing to do with extrusion, but a nice reminder that SVG text tooling goes well beyond shadows. For a much deeper look — measuring text width, seamless looping, and path-based physics — see the Text on a Path demo.
Bonus 2: real geometry with Three.js
All four approaches above are 2D illusionists. Three.js can produce actual 3D geometry via TextGeometry and ExtrudeGeometry, with a real depth axis, perspective, and lighting.
Spark Joy!
Pros: Real geometry means real reflections, real shadows, real camera angles. The effect never breaks down regardless of font size or extrusion depth.
Cons: Three.js and a JSON font file add meaningful bundle weight. You also need to convert the font into a JSON format. The text also lives entirely in a WebGL canvas and is thus invisible to screen readers and search engines.
SVG turbulence clouds
Above the clouds
SVG filters aren’t just for text shadows. The cloud backdrop here uses an feTurbulence filter with a slowly drifting baseFrequency to produce organic cloud shapes from fractal noise. The same pipeline is available to HTML elements, SVG elements, and CSS backdrop-filter — a surprisingly versatile compositing primitive.