# Unity Integration for JSAR: Native Plug-in API and Architecture

> Discover the native Unity integration for JSAR. Learn how the compiled plug-in controls the Transmute runtime for document management, XR sync, and frame rendering via exported C functions.

- Repository: [M Creative Lab/jsar-runtime](https://github.com/m-creativelab/jsar-runtime)
- Tags: architecture
- Published: 2026-03-06

---

**JSAR provides a native Unity plug-in packaged as a compiled `.so` or `.dylib` library that exposes C-style exported functions through [`src/runtime/unity_entry.cpp`](https://github.com/m-creativelab/jsar-runtime/blob/main/src/runtime/unity_entry.cpp), enabling Unity to control the Transmute runtime via the `UnityEmbedder` class for document management, XR synchronization, and frame rendering.**

The `m-creativelab/jsar-runtime` repository delivers JSAR (the Transmute runtime) as a native Unity plug-in, enabling developers to embed JavaScript XR content directly within Unity applications. This **Unity integration for JSAR** implements a thin C++ bridge in [`src/runtime/unity_entry.cpp`](https://github.com/m-creativelab/jsar-runtime/blob/main/src/runtime/unity_entry.cpp) that connects Unity's native interface pointers with the runtime's graphics, XR, and content management subsystems.

## How the Unity Plug-in Architecture Works

The integration follows a layered architecture where Unity loads the native library and exchanges interface pointers with the JSAR runtime through a dedicated embedder class.

### Unity Host Layer and Entry Points

When Unity loads the plug-in, it automatically invokes `UnityPluginLoad` and supplies an `IUnityInterfaces` pointer. This function, defined in [`src/runtime/unity_entry.cpp`](https://github.com/m-creativelab/jsar-runtime/blob/main/src/runtime/unity_entry.cpp) (lines 73-90), initializes the bridge between Unity and JSAR. On Android platforms, the plug-in additionally reads system properties such as `JSAR_WEBGL_PLACEHOLDERS` and `JSAR_DEBUG_ENABLED` via `OnPlatformSetup` to configure environment variables before the runtime starts.

### The UnityEmbedder Bridge Class

The `UnityEmbedder` class (lines 38-66 in [`src/runtime/unity_entry.cpp`](https://github.com/m-creativelab/jsar-runtime/blob/main/src/runtime/unity_entry.cpp)) inherits from `TrEmbedder` (defined in [`src/runtime/embedder.hpp`](https://github.com/m-creativelab/jsar-runtime/blob/main/src/runtime/embedder.hpp)) and stores references to `IUnityGraphics` and `IUnityLog`. This class translates Unity lifecycle events—initialize, shutdown, and render passes—into calls that the JSAR runtime understands, maintaining synchronization between the two environments.

### Graphics Device Initialization

During `kUnityGfxDeviceEventInitialize`, the embedder queries the active renderer type using `graphics->GetRenderer()` and invokes `RHIFactory::CreateRHI` to instantiate the correct rendering hardware interface. This mechanism links Unity's backend (OpenGL, Vulkan, or Metal) directly to JSAR's renderer, ensuring frame buffers and graphics contexts remain compatible.

## Core Unity Integration APIs

The plug-in exposes a suite of C functions that Unity can invoke via `DllImport` attributes in C# scripts.

### Runtime Configuration and Lifecycle

Before starting the runtime, Unity must call `TransmuteUnity_Configure`, which parses a JSON string to set the application cache directory, proxy settings, and XR support flags. After configuration, `TransmuteUnity_Start` bootstraps the runtime and prepares the document environment.

### Content Management (Documents)

Unity controls JSAR documents through identifiers returned by `TransmuteUnity_Open`. This function accepts a URL and a `UnityDocumentRequestInit` structure (containing `disableCache` and `isPreview` booleans) and returns a document ID that can be used with `TransmuteUnity_Pause`, resume, and close operations. These functions wrap the internal `ContentManager` to manage the lifecycle of JavaScript contexts.

### XR and Input Synchronization

For XR applications, the plug-in provides specific functions to synchronize device state:

- `TransmuteUnity_ConfigureXRDevice` initializes XR support within the runtime
- `TransmuteUnity_GetMainControllerInputSource` retrieves controller identifiers
- `TransmuteUnity_SetInputSourceRayPose` updates input source poses from Unity's tracking system
- `TransmuteUnity_SetViewerTransformFromTRS` synchronizes the viewer's position and orientation with the JSAR `xrDevice` subsystem

These functions ensure that head tracking and controller input in Unity are accurately reflected in the JavaScript XR environment.

### Rendering Callbacks

Unity retrieves a render-event function pointer via `TransmuteUnity_GetRenderEventFunc`. Unity scripts then invoke this pointer each frame using `GL.IssuePluginEvent`, passing an integer identifier that maps to specific render passes (`kBeforeRenderingPass`, `kOpaquesRenderPass`, `kAfterRenderingPass`). The plug-in forwards these events to JSAR's corresponding callbacks (`onBeforeRendering`, `onOpaquesRenderPass`, etc.), allowing JavaScript code to participate in Unity's render loop.

## Implementing the Integration in C#

Below is a minimal Unity C# script demonstrating how to import and use the JSAR native plug-in:

```csharp
using System;
using System.Runtime.InteropServices;
using UnityEngine;

public class JsarBridge : MonoBehaviour
{
    [DllImport("jsar_runtime")]
    private static extern void UnityPluginLoad(IntPtr unityInterfaces);

    [DllImport("jsar_runtime")]
    private static extern IntPtr TransmuteUnity_GetRenderEventFunc();

    [DllImport("jsar_runtime")]
    private static extern bool TransmuteUnity_Configure(string configJson);

    [DllImport("jsar_runtime")]
    private static extern bool TransmuteUnity_Start();

    [DllImport("jsar_runtime")]
    private static extern int TransmuteUnity_Open(string url, UnityDocumentRequestInit init);

    [StructLayout(LayoutKind.Sequential)]
    public struct UnityDocumentRequestInit
    {
        public bool disableCache;
        public bool isPreview;
    }

    void Awake()
    {
        // Unity automatically invokes UnityPluginLoad when the plug-in loads
    }

    void Start()
    {
        var config = "{\"applicationCacheDirectory\":\"/data/local/tmp/jsar\",\"isXRSupported\":true}";
        if (!TransmuteUnity_Configure(config))
            Debug.LogError("JSAR configuration failed");

        if (!TransmuteUnity_Start())
            Debug.LogError("Failed to start JSAR");

        var init = new UnityDocumentRequestInit { disableCache = false, isPreview = false };
        int docId = TransmuteUnity_Open("https://example.com", init);
        Debug.Log($"Opened JSAR document, id={docId}");
    }

    void Update()
    {
        // Trigger JSAR rendering during the opaque render pass
        GL.IssuePluginEvent(TransmuteUnity_GetRenderEventFunc(), 1); // 1 == kOpaquesRenderPass
    }
}

```

## Key Source Files and Build Configuration

The Unity integration spans several source files and build configurations:

- **[`src/runtime/unity_entry.cpp`](https://github.com/m-creativelab/jsar-runtime/blob/main/src/runtime/unity_entry.cpp)** – Central plug-in implementation containing exported C functions, the `UnityEmbedder` class definition, render-event handling, and XR/input APIs.
- **[`src/runtime/embedder.hpp`](https://github.com/m-creativelab/jsar-runtime/blob/main/src/runtime/embedder.hpp)** – Base `TrEmbedder` class that defines the generic embedder lifecycle inherited by `UnityEmbedder`.
- **[`src/runtime/renderer/render_api.hpp`](https://github.com/m-creativelab/jsar-runtime/blob/main/src/runtime/renderer/render_api.hpp)** – Abstract RHI factory used by the embedder to select the correct graphics backend based on Unity's active renderer.
- **[`src/runtime/xr/device.hpp`](https://github.com/m-creativelab/jsar-runtime/blob/main/src/runtime/xr/device.hpp)** – XR device abstraction accessed by Unity-specific functions for pose and input handling.
- **`thirdparty/headers/Unity/*`** – Unity native SDK headers ([`IUnityGraphics.h`](https://github.com/m-creativelab/jsar-runtime/blob/main/IUnityGraphics.h), [`IUnityLog.h`](https://github.com/m-creativelab/jsar-runtime/blob/main/IUnityLog.h), etc.) required for compilation.
- **[`CMakeLists.txt`](https://github.com/m-creativelab/jsar-runtime/blob/main/CMakeLists.txt)** – Build configuration that produces the `jsar_runtime` library target, compiling the plug-in for specific platforms.

## Summary

- JSAR integrates with Unity through a native plug-in compiled as `jsar_runtime` (`.so` or `.dylib`).
- The `UnityEmbedder` class in [`src/runtime/unity_entry.cpp`](https://github.com/m-creativelab/jsar-runtime/blob/main/src/runtime/unity_entry.cpp) bridges Unity's `IUnityInterfaces` with the Transmute runtime.
- Graphics backends are auto-detected via `RHIFactory::CreateRHI` to match Unity's active renderer (OpenGL, Vulkan, Metal).
- XR synchronization uses functions like `TransmuteUnity_SetViewerTransformFromTRS` to mirror Unity's tracking data in JSAR.
- Content management relies on document IDs returned by `TransmuteUnity_Open`, which wraps the internal `ContentManager`.
- Rendering integration uses `TransmuteUnity_GetRenderEventFunc` and `GL.IssuePluginEvent` to insert JSAR draw calls into Unity's render passes.

## Frequently Asked Questions

### How does Unity initialize the JSAR runtime?

Unity loads the native plug-in automatically, triggering `UnityPluginLoad` in [`src/runtime/unity_entry.cpp`](https://github.com/m-creativelab/jsar-runtime/blob/main/src/runtime/unity_entry.cpp) with an `IUnityInterfaces` pointer. The Unity script then calls `TransmuteUnity_Configure` with a JSON configuration string (specifying cache paths and XR support), followed by `TransmuteUnity_Start` to boot the runtime according to the source code implementation.

### Which graphics APIs are supported by the JSAR Unity plug-in?

The plug-in supports OpenGL, Vulkan, and Metal. During initialization, the `UnityEmbedder` queries Unity's active renderer via `IUnityGraphics->GetRenderer()` and uses `RHIFactory::CreateRHI` to instantiate the matching rendering backend, as implemented in the graphics device initialization logic.

### How does the JSAR Unity integration handle XR device input?

The plug-in exposes specific C functions including `TransmuteUnity_ConfigureXRDevice`, `TransmuteUnity_GetMainControllerInputSource`, and `TransmuteUnity_SetInputSourceRayPose` that Unity scripts call to synchronize controller IDs and poses. These functions write directly to the JSAR `xrDevice` subsystem defined in [`src/runtime/xr/device.hpp`](https://github.com/m-creativelab/jsar-runtime/blob/main/src/runtime/xr/device.hpp).

### What functions does Unity use to control JSAR documents?

Unity opens documents using `TransmuteUnity_Open`, which accepts a URL and a `UnityDocumentRequestInit` struct and returns a document ID. This ID is subsequently used with `TransmuteUnity_Pause`, resume, and close functions to manage the JavaScript context lifecycle through the internal `ContentManager`.