EXPLORE COLLECTIONS LEADERBOARD PRICING DOCS BLOG DISCORD
← Blog Engineering 5 min read

The 2KB Toon Outline Shader Hiding in Babylon.js

Published April 30, 2026 · Discovery story

When I went looking for the right way to add a Persona / Genshin-style toon outline to VXLVERSE, the usual suspects turned up: inverted-hull mesh duplication, custom Sobel-filter shaders, forum threads with partial code, NME node-material graphs that looked great in playgrounds but didn't always translate to a real production scene.

Then I tried EdgeDetectionPostProcess — a screen-space outline shipped in Babylon's post-processes asset library that reads the depth + normal G-buffer. Looked clean. Three lines of integration. Done.

This is the article I wish I'd found earlier.

What it actually is

EdgeDetectionPostProcess lives in @babylonjs/post-processes (the asset library, not core). It's a screen-space pass that:

  • Samples the geometry buffer (depth + normal textures)
  • Detects edges where depth or normal changes sharply (silhouettes, hard edges)
  • Draws those edges in a flat color over the rendered frame

The result: clean uniform-thickness outlines around silhouettes and hard edges, regardless of camera distance. The exact look you see in Persona 5, Hi-Fi Rush, and Honkai.

The file weight is laughable. edgeDetectionPostProcess.js is 5.4KB unminified. After tree-shake + gzip you're paying about 2KB of bundle for the entire effect. The shader is in a separate file, ~200 lines of GLSL.

Wiring it up

Three lines:

import { EdgeDetectionPostProcess }
  from "@babylonjs/post-processes/edgeDetection/edgeDetectionPostProcess";

const outline = new EdgeDetectionPostProcess("toon", scene, 1.0, camera);
outline.edgeColor = Color3.FromHexString("#000000");
outline.edgeIntensity = 0.08;  // 0..1, very subtle
outline.edgeWidth = 0.125;    // 0.125..1, hairline

That's it. Geometry buffer activates automatically (the post-process imports geometryBufferRendererSceneComponent). No manual G-buffer setup. No NME graph. No custom shaders.

Tuning for "tasteful" not "comic book"

Babylon's defaults are 0.2 / 0.2 — already pretty subtle. We tuned ours lower for VXLVERSE because the look we wanted was "Genshin Impact hairline" not "Borderlands chunky". The hard floor is edgeWidth: 0.125 (any lower and Babylon clamps).

Reference values

  • Hairline (Genshin): intensity 0.08, width 0.125
  • Tasteful (Persona 5): intensity 0.2-0.3, width 0.2
  • Comic book (Borderlands): intensity 0.5+, width 0.5+

The trap nobody mentions: camera switching

In an editor that toggles between an orbit camera and a fly cam, the post-process attaches to ONE camera at construction. When the user toggles to the other camera, the outline disappears.

Babylon's DefaultRenderingPipeline takes a list of cameras. EdgeDetectionPostProcess takes one. There's no equivalent attach-to-multiple API on the post-process itself.

The fix: listen to scene.onActiveCameraChanged and rebuild the post-process on the new active camera. Cheap operation, instant flicker (1-2 frames) on toggle, then clean.

scene.onActiveCameraChanged.add(() => {
  const newCam = scene.activeCamera;
  if (!newCam || newCam === this.outlineCamera) return;
  this.outline?.dispose(this.outlineCamera);
  this.outline = new EdgeDetectionPostProcess("toon", scene, 1.0, newCam);
  this.outlineCamera = newCam;
  // Re-write live settings...
});

Performance — pair it with SSAO

Standalone, the outline costs about 1.5-2ms per frame. Most of that is the geometry buffer pass (rasterizing the scene a second time to get depth + normal textures).

If your scene already runs SSAO, the geometry buffer is already being computed — you pay nothing extra to add the outline. Pairing the two is essentially free for the second consumer.

In VXLVERSE the Inspector shows an amber "+SSAO for free" hint when the outline is on but SSAO is off, nudging users toward the cheaper combination.

Why this isn't more well-known

I have a theory: @babylonjs/post-processes is a separate package from @babylonjs/core. It needs its own install, separate import path. Most "getting started" tutorials don't touch it. Most CodePens import only core.

The result: when you search "toon outline in Babylon", a lot of the answers point at the inverted-hull technique (which can fight with anti-aliasing in some cases) or NME node materials (which require shader graph wrangling). The simpler answer is sitting in a sibling package that's easy to overlook.

When to use it

  • You want a Persona / Genshin / Hi-Fi Rush look.
  • You don't want to write GLSL.
  • You're already running SSAO (or willing to pay 1.5ms).
  • You can live with screen-space fixed-thickness outlines (vs. world-space scaled).

If you're shipping Babylon-based 3D in 2026 and you haven't tried this, you're probably reinventing a wheel that's already in your node_modules.

VXLVERSE uses this exact technique to give every scene an optional toon look. Try the editor — toggle the outline in the Post Process panel and see how 2KB of code transforms a scene.