How to Use the Native Embedder API (TrEmbedder) in JSAR

To use the native embedder API in JSAR, subclass the abstract TrEmbedder class from src/runtime/embedder.hpp, implement the pure virtual onEvent method, configure the runtime via configure(), and drive the rendering loop through the four lifecycle hooks: onBeforeRendering(), onOpaquesRenderPass(), onTransparentsRenderPass(), and onAfterRendering().

The native embedder API allows C++ applications to host the JSAR runtime within game engines like Unity, Unreal, or custom OpenGL/DirectX applications. Found in the m-creativelab/jsar-runtime repository, the TrEmbedder class serves as the bridge between your host engine and the JSAR content system, managing initialization, per-frame rendering, and native event handling.

Architecture of the Native Embedder API

The native embedder API centers on the TrEmbedder abstract class defined in src/runtime/embedder.hpp. This base class owns a TrConstellation instance—the central orchestrator declared in src/runtime/constellation.hpp that manages the renderer, content manager, and XR device.

Key architectural components include:

  • TrEmbedder: The base class that host applications must inherit from. It encapsulates the TrConstellation runtime core and provides configuration, lifecycle management, and event handling APIs.
  • Lifecycle Hooks: Virtual methods defined in src/runtime/embedder.cpp that the host calls each frame to execute rendering passes and advance the simulation.
  • Event System: The pure virtual onEvent method that implementations must override to process native events such as RPC requests from JavaScript.
  • XR Integration: Optional configuration via configureXrDevice to enable stereo rendering for AR/VR headsets.

Creating a Custom Embedder Subclass

To integrate JSAR into your application, create a concrete subclass of TrEmbedder and implement the required virtual methods.

Constructor and Renderer Setup

Inside your subclass constructor, specify the host engine type and initialize the Rendering Hardware Interface (RHI). The TrEmbedder constructor automatically instantiates the constellation member, allowing immediate access to constellation->renderer to set up your graphics backend.

#include <runtime/embedder.hpp>
#include <renderer/render_api.hpp>

class MyEmbedder : public TrEmbedder {
public:
    MyEmbedder() : TrEmbedder(TrHostEngine::None) {
        auto renderer = constellation->renderer;
        auto rhi = RHIFactory::CreateRHI(kUnityGfxRendererOpenGLCore, constellation.get());
        renderer->setRHI(rhi);
    }
    
    // onEvent implementation required
};

Implementing Event Handling

You must implement the pure virtual onEvent method to handle incoming native events. This method receives an events_comm::TrNativeEvent reference and a std::shared_ptr<TrContentRuntime> for sending responses.

bool onEvent(events_comm::TrNativeEvent &event,
             std::shared_ptr<TrContentRuntime> content) override {
    if (event.type == events_comm::TrNativeEventType::RpcRequest) {
        auto request = event.detail<events_comm::TrRpcRequest>();
        if (request.method == "ping") {
            events_comm::TrRpcResponse resp(true);
            resp.message = "pong";
            content->respondRpcRequest(resp, event.id);
            return true;
        }
    }
    return false;
}

Runtime Lifecycle Management

The embedder follows a strict initialization-to-shutdown lifecycle controlled by the host application.

Configuration and Startup

Call configure(storageDir, proxy, enableXR) with a cache directory path, optional network proxy, and boolean XR flag, followed by start() to initialize the runtime:

MyEmbedder embedder;
if (!embedder.configure("./jsar_cache", "", false)) {
    std::cerr << "Configuration failed\n";
    return -1;
}
if (!embedder.start()) {
    std::cerr << "Startup failed\n";
    return -1;
}

Per-Frame Rendering Hooks

Drive the rendering loop by calling these methods in sequence every frame. These methods are implemented in src/runtime/embedder.cpp and forward calls to the internal TrConstellation:

  1. onBeforeRendering() – Prepares the frame and updates the scene.
  2. onOpaquesRenderPass() – Renders opaque geometry.
  3. onTransparentsRenderPass() – Renders transparent geometry.
  4. onAfterRendering() – Finalizes the frame and submits command buffers.
for (int frame = 0; frame < 600; ++frame) {
    embedder.onBeforeRendering();
    embedder.onOpaquesRenderPass();
    embedder.onTransparentsRenderPass();
    embedder.onAfterRendering();
    
    std::this_thread::sleep_for(std::chrono::milliseconds(16));
}

When the host application exits, call shutdown() to clean up all internal services and release resources.

Enabling XR Stereo Rendering

For XR applications, enable stereo rendering during configuration and provide device parameters:

// Enable XR in configure
embedder.configure("./jsar_cache", "", true);

// Configure XR device properties
xr::TrDeviceInit xrInit;
xrInit.active = true;
xrInit.stereoRenderingMode = xr::StereoRenderingMode::SinglePass;
embedder.configureXrDevice(xrInit);

You can monitor runtime performance via getFps() and getUptime(), or retrieve version information using getVersion() during the frame loop.

Complete Implementation Example

The file src/examples/desktop_opengl.cpp provides a reference implementation named DesktopEmbedder. Below is a minimal standalone example combining all required components:

#include <runtime/embedder.hpp>
#include <renderer/render_api.hpp>
#include <iostream>
#include <thread>
#include <chrono>

class MinimalEmbedder : public TrEmbedder {
public:
    MinimalEmbedder() : TrEmbedder(TrHostEngine::None) {
        auto rhi = RHIFactory::CreateRHI(kUnityGfxRendererOpenGLCore, constellation.get());
        constellation->renderer->setRHI(rhi);
    }

    bool onEvent(events_comm::TrNativeEvent &event,
                 std::shared_ptr<TrContentRuntime> content) override {
        return false;
    }
};

int main() {
    MinimalEmbedder embedder;
    
    if (!embedder.configure("./jsar_cache", "", false)) {
        return -1;
    }
    
    if (!embedder.start()) {
        return -1;
    }
    
    for (int i = 0; i < 600; ++i) {
        embedder.onBeforeRendering();
        embedder.onOpaquesRenderPass();
        embedder.onTransparentsRenderPass();
        embedder.onAfterRendering();
        std::this_thread::sleep_for(std::chrono::milliseconds(16));
    }
    
    embedder.shutdown();
    return 0;
}

Summary

  • Subclass TrEmbedder from src/runtime/embedder.hpp to create a host-specific implementation.
  • Implement onEvent to handle RPC requests and native input events from the JavaScript runtime.
  • Call configure() and start() to initialize; call shutdown() to destroy the runtime cleanly.
  • Drive rendering via the four lifecycle hooks: onBeforeRendering, onOpaquesRenderPass, onTransparentsRenderPass, and onAfterRendering.
  • Access telemetry through getFps() and getUptime() for performance monitoring.
  • Enable XR by passing true to configure() and providing a filled xr::TrDeviceInit structure to configureXrDevice().

Frequently Asked Questions

What is TrEmbedder in JSAR?

TrEmbedder is the abstract C++ base class defined in src/runtime/embedder.hpp that provides the interface between host applications and the JSAR runtime. It owns the TrConstellation core and declares pure virtual methods, including onEvent, that concrete implementations must provide to handle communication between the host engine and JavaScript content.

How do I handle RPC requests in TrEmbedder?

Override the onEvent method to intercept events of type events_comm::TrNativeEventType::RpcRequest. Use event.detail<events_comm::TrRpcRequest>() to deserialize the request payload, then call content->respondRpcRequest(response, event.id) to send data back to the JavaScript context, as shown in src/examples/desktop_opengl.cpp.

Can I use TrEmbedder without XR support?

Yes. Pass false as the third parameter to configure(const std::string& storageDir, const std::string& proxy, bool enableXR). The runtime operates in standard mono rendering mode unless you explicitly enable XR and call configureXrDevice() with a valid xr::TrDeviceInit configuration.

What rendering backends are supported by TrEmbedder?

The embedder supports multiple rendering backends through the RHI abstraction layer. You can initialize OpenGL, Vulkan, or engine-specific backends (Unity, Unreal, Cocos) by passing the appropriate renderer type constant to RHIFactory::CreateRHI(), then calling constellation->renderer->setRHI() with the resulting interface.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →