Running three.js inside Unreal Engine¶
Render an actual three.js scene (HTML canvas + WebGL) as a live surface inside UE 5.7.
Plugin options¶
| Plugin | Type | Pros | Cons |
|---|---|---|---|
| Web Browser + Web Browser Widget | Built-in | Free, in the box | CEF-based, render-to-widget only |
| WebUI (tracerinteractive) | Community ★ | Render-to-material, modern CEF | Community-maintained |
| Ultralight UE | Community | Lower memory | Limited WebGL |
| Coherent Gameface | Marketplace | AAA-grade | Enterprise pricing |
Architecture¶
graph LR
T[three.js index.html<br/>WebGL canvas] -->|loaded by| W[CEF Web Browser widget]
W -->|render texture| R[Render Target 2D]
R -->|sampler in material| M[MaterialInstanceDynamic]
M -->|applied to| Q[Mesh / Widget in world]
Recipe — built-in Web Browser¶
- Enable plugins:
Web Browser,Web Browser Widget→ restart - Place the three.js scene:
<Project>/Content/UI/three/index.htmlwith bundled JS /three.module.js - Widget Blueprint — drag a
WebBrowserwidget onto canvas; set initial URL tofile:///%GAMEDIR%/Content/UI/three/index.html - 3D placement — drop a
WidgetComponenton an actor, setWidget Class = your UMG widget, Widget Space = World - Tune:
Draw Size = 2048×1024, Pivot = (0.5, 0.5)
For render-to-material (paint the three.js scene on any mesh face):
WidgetComp->SetWidgetClass(MyWidgetClass);
WidgetComp->SetDrawSize(FVector2D(2048,1024));
WidgetComp->SetWidgetSpace(EWidgetSpace::World);
WidgetComp->SetTwoSided(true);
// internal: WidgetComp creates a render target you can sample via the material
Then in a Material:
- Add
Texture Samplereferencing the WidgetComponent's render target - Connect to Emissive Color
Recipe — WebUI¶
WebUI exposes a Material that already samples the live CEF surface. Drop MIDWebUI on any mesh — done.
UWebUIComponent* Web = NewObject<UWebUIComponent>(this);
Web->URL = TEXT("file:///%GAMEDIR%/Content/UI/three/index.html");
Web->RegisterComponent();
Mesh->SetMaterial(0, Web->CreateMaterialInstanceForSurface());
Communicating UE ↔ three.js¶
CEF exposes cefQuery-style messaging. From JS:
window.ueQuery({
request: JSON.stringify({ event: 'onSpawn', x: 5, y: 0 }),
onSuccess: r => console.log(r),
onFailure: e => console.error(e),
});
From UE Blueprint, bind the OnUrlChanged / OnJavaScriptDialog delegate or use the WebUI bridge to receive JSON messages.
Audio¶
CEF browser has its own audio output — routed to OS audio. If you need UE's audio system to capture it:
- WASAPI loopback capture (Windows) → write to a
USoundWaveProcedural - Or use a media source via Media Framework with a WebRTC bridge
Limitations¶
- CEF transparency depends on the build — not all transparent backgrounds render through
- WebGL2 works; WebGPU is iffy depending on CEF version
- Performance: each browser surface is a separate Chromium process; 4K canvases at 60Hz are noticeable cost
Better alternatives in some cases¶
- Spout: run three.js in an Electron window with Spout4three.js, pipe the GPU texture directly to UE. No CEF tax, sub-frame latency.
- NDI: same idea over network — useful for cross-machine three.js → UE.
See also¶
- Pixel Streaming — the reverse direction
- UE in three.js
- Plugins → UI