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.

Overview

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.

Quickstart

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.

What you get

Five things the wrapper does for you

The hard parts of the integration — handled, and invisible when you get them right.

Zero integration boilerplate

A script tag and one function call. No build tooling, no framework lock-in, no bundler config.

Depth-correct compositing

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.

Domain license or per-user sign-in

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.

Self-hostable

The serverAddress option points the SDK at a private udCloud tenant for customers running their own infrastructure.

Versioned CDN

Immutable versioned paths. Pin your version; upgrade on your schedule.

Requirements

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.

Cross-origin isolation

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

ArcGIS Maps SDK 5.x on the page

The 5.x SDK is ESM-only. The type="module" attribute is required.

<script type="module" src="https://js.arcgis.com/5.0/"></script>

A sign-in path for your users

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.

API Reference

attach() options and controller

udSDKArcGIS.attach(sceneElement, options) returns a controller. Defaults are sensible; override only what you need.

Full options reference

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,
});

Controller API

`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();
Under the hood

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.

RenderNode integration

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.

Camera matrices each frame

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.

Emscripten worker bootstrap

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.

OAuth flow preserves the user gesture

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.

Domain-first sign-in

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.

Known limitations

Things to know before you ship

The rough edges in the current build. Workarounds are noted where they exist.

One instance per page

Emscripten's Module is a global. You can't run two independent udSDK bridges on one document.

serverAddress only honored on the OAuth path

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.

Cross-origin isolation is mandatory

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.

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.