Your ArcGIS JS scene. Full-resolution point clouds. No GPU.
Two tags. Full-resolution point clouds.
The same engine powered Leica JetStream, trusted by 30,000+ geospatial professionals worldwide. Protected under Unlimited Detail patents.
Esri's native point cloud layer decimates data above a threshold your sensor already exceeded. Drop in udSDK and your ArcGIS JS scene renders the full dataset on CPU. No changes to your scene setup. No GPU.
A drop-in bridge. Not a rewrite.
udsdk-arcgis.js subclasses ArcGIS's RenderNode and inserts it into the composite-color render pipeline. Point cloud pixels share the scene's depth buffer, so they interleave correctly with terrain, 3D buildings, and mesh layers — not painted on top. Without the wrapper, this integration is roughly 500 lines of emscripten bootstrap, OAuth popup plumbing, and WebGL RenderNode code. With it, three lines.
A complete, runnable page
Two script tags and one function call. Opens on a LiDAR scan of Japan over Esri's Hybrid basemap with correct occlusion against terrain. The live demo on the right runs exactly this code.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>udSDK + ArcGIS</title>
<script type="module" src="https://js.arcgis.com/5.0/"></script>
<script src="https://nuclideon.com/cdn/integrations/esri/1.0.0/udsdk-arcgis.js"></script>
<style> html, body, arcgis-scene { height: 100%; margin: 0; display: block; } </style>
</head>
<body>
<arcgis-scene
id="scene"
basemap="hybrid"
ground="world-topobathymetry"
camera-position="139.160621,34.830097,4181.10"
camera-heading="333.51"
camera-tilt="61.71"
>
<arcgis-zoom slot="top-left"></arcgis-zoom>
<arcgis-compass slot="top-left"></arcgis-compass>
<arcgis-home slot="top-left"></arcgis-home>
</arcgis-scene>
<script>
udSDKArcGIS.attach(document.getElementById("scene"), {
models: ["https://nucl-demo.s3.amazonaws.com/Japan/JapanALS_Split/1_0_0.uds"],
});
</script>
</body>
</html>
Pin to a versioned path. The CDN host prefix may change — the version segment will not.
Five things the wrapper does for you
The hard parts of the integration — handled, and invisible when you get them right.
A script tag and one function call. No build tooling, no framework lock-in, no bundler config.
Point clouds interleave with ArcGIS 3D content via a shared depth buffer. Not a flat overlay — terrain and 3D buildings correctly occlude and are occluded.
Two paths to udCloud: a Nuclideon-issued domain license signs all visitors in silently on that domain (no popup, no account), or individual users sign in with their own udCloud accounts. `attach()` tries the silent domain path first and falls back to OAuth.
The serverAddress option points the SDK at a private udCloud tenant for customers running their own infrastructure.
Immutable versioned paths. Pin your version; upgrade on your schedule.
Three things your host page must provide
Before you wire up `attach()`, the page serving your HTML needs to satisfy these. The cross-origin isolation requirement has real deployment implications — read it carefully.
udSDK uses SharedArrayBuffer for its worker pool, which requires the host document to be cross-origin isolated. Send these headers on the HTML document. credentialless is the easier choice — require-corp forces every cross-origin resource (ArcGIS CDN, basemap tiles, udCloud, your .uds hosts) to send its own CORP header, which is brittle across origins you don't control. These headers are per-document, not site-wide — scope them to the viewer path in your CDN or reverse proxy.
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: credentialless
The 5.x SDK is ESM-only. The type="module" attribute is required.
<script type="module" src="https://js.arcgis.com/5.0/"></script>
Two options, pick one. Domain license: Nuclideon issues a license bound to your hosting domain (the way nuclideon.com is set up). Any page served from that domain signs visitors in silently — no popup, no per-user account. Talk to us to arrange one. Per-user udCloud accounts: each end user signs in with their own account via the OAuth popup at udcloud.nuclideon.com. .uds point cloud files can be hosted anywhere the browser can fetch from with CORS — they don't need to live in udCloud.
attach() options and controller
udSDKArcGIS.attach(sceneElement, options) returns a controller. Defaults are sensible; override only what you need.
All options are optional. Pass a bare `{}` to take defaults.
udSDKArcGIS.attach(sceneElement, {
// Name shown on the udCloud OAuth consent screen.
// Default: "arcgis-<window.location.hostname>"
clientName: "Acme Portal",
// Models to auto-load after sign-in. Strings or {url, resolution, slot} objects.
models: [
"https://my-bucket.example.com/site.uds",
{ url: "https://.../other.uds", resolution: 2.5, slot: -1 },
],
// "auto" shows a built-in sign-in button when needed; false = customer drives it
signInUI: "auto",
signInLabel: "Sign in to udCloud",
// Point at a self-hosted or regional udCloud. Default: https://udcloud.nuclideon.com/
serverAddress: "https://udcloud.acme.internal/",
// "auto" tries silent domain-license sign-in first; false always shows the OAuth popup
tryDomainLogin: "auto",
// Attach the render node in enabled state. Set false to start disabled.
autoEnable: true,
// Override the SDK CDN base. Defaults to the udSDK release this
// wrapper version was pinned to at build time
// (https://nuclideon.com/cdn/udsdk/2.6.1/js/).
baseUrl: null,
// Callback for emscripten loader progress strings
onStatus: (text) => console.log(text),
// How long to wait for the ArcGIS SDK to appear on the page
arcgisTimeoutMs: 30000,
});
`attach()` returns a controller exposing sign-in state, model management, a rendering toggle, and an EventTarget for lifecycle events.
const ud = await udSDKArcGIS.attach(scene, { /* ... */ });
// Sign-in state
ud.signedIn; // boolean
await ud.signIn(); // manual OAuth (must be from user gesture)
// Model management
await ud.loadModel("https://.../extra.uds");
await ud.loadModel({ url: "...", resolution: 2.0, slot: 0 });
// Rendering toggle
ud.enabled = false; // hide point clouds without unloading
ud.enabled = true;
// Underlying objects, if you want to reach in
ud.view; // the ArcGIS SceneView
ud.renderNode; // the RenderNode subclass instance
// Events (EventTarget)
ud.addEventListener("ready", () => {});
ud.addEventListener("signed-in", (e) => {
// e.detail.method is "domain" (silent domain-license sign-in) or "oauth" (popup)
});
ud.addEventListener("sign-in-error", (e) => {});
ud.addEventListener("model-loaded", (e) => { /* e.detail.url, e.detail.handle */ });
ud.addEventListener("model-error", (e) => { /* e.detail.url, e.detail.code */ });
// Teardown
ud.destroy();
How the bridge actually works
A walk through the render pipeline the wrapper installs. Skip this section if you just want to ship; it's here for the integration leads who need to know what's running inside their scene before they sign off.
The wrapper subclasses @arcgis/core/views/3d/webgl/RenderNode.js and inserts into the composite-color pipeline. It grabs the scene's composite color texture and depth stencil attachment, then runs two shader passes: a blit of the ArcGIS scene, then a pass that samples udSDK's color + depth buffers and writes gl_FragDepth. The hardware depth test then handles occlusion against terrain and 3D layers automatically.
View and projection matrices from ArcGIS's camera are fed into udSDKJS_SetMatrix("view", ...) and udSDKJS_SetMatrix("projection", ...) every frame, so udSDK renders from the same viewpoint as the scene.
udSDK is a threaded emscripten build. Since the wrapper loads cross-origin from the CDN, it fetches udSDKjs.js as text, wraps it in a Blob, and passes the Blob URL to Module.mainScriptUrlOrBlob so the pthread pool can spawn same-origin workers. Browsers refuse new Worker(crossOriginURL), so this detour is unavoidable.
udSDKJS_ConnectStart returns an approve URL. The wrapper navigates an already-opened popup to it from inside the click handler — popups opened after an await get blocked. udSDKJS_ConnectComplete polls udCloud for completion.
Before showing any UI, the wrapper tries udSDKJS_CreateSharedFrom_udCloud(clientName, null). The null key argument tells udCloud to authenticate against a domain license bound to the serving origin. If the domain is licensed, no popup appears and every visitor is signed in. If it isn't, the built-in OAuth button renders and the user signs in with their own udCloud account.
Things to know before you ship
The rough edges in the current build. Workarounds are noted where they exist.
Emscripten's Module is a global. You can't run two independent udSDK bridges on one document.
Domain login ignores the serverAddress override in the current udSDK build. Fix is pending on the SDK side. Customers needing a custom tenant today should set tryDomainLogin: false until the fix lands.
A page that can't set the COOP/COEP headers — for example, an Esri-hosted portal where the headers are out of your control — can't use the drop-in as-is. You'll need a reverse proxy or to iframe the viewer into a page you do control.
Why the Unlimited Detail engine runs on the CPU — the architecture behind the ArcGIS wrapper, explained.
How the same engine powering this SDK ran in production at enterprise scale.
Licensing options, platform support, and integration documentation.
Ship point clouds in your ArcGIS scene.
The script is live at the CDN. Wire it up in an afternoon. If you need a private tenant or a self-hosted udCloud, talk to us first.