Skip to content

Guides

Set up or build a PlayRoam add-on

Updated October 9, 2026 · 9 min read

Picture add-ons change the received video in PlayRoam for Windows with Pro. They don't change how your console renders the game or how the game works. Open Add-ons to see installed add-ons and their status. Without Pro, the app shows Add-ons come with Pro, and you can't turn on or run an add-on.

You can run one picture add-on at a time: NVIDIA Neural Rendering or one third-party add-on. You can also leave all add-ons off. Each add-on receives real source frames at their original size, in SDR RGBA8. This means standard dynamic range pictures with 8-bit red, green, blue and alpha channels. Processing happens before frame generation and upscaling. Add-ons don't receive generated frames or HDR pictures.

2× AI frame generation uses RIFE, whose model and runtime are included in the PlayRoam installer. Choose it under FPS boost. See frame-generation choices.

Install an add-on

Open the add-ons folder for your Windows user:

%LOCALAPPDATA%\PlayRoam\addons\

Give each third-party add-on its own subfolder. Keep addon.json, the file that describes the add-on, beside its x64 DLL:

%LOCALAPPDATA%\PlayRoam\addons\sharpen\addon.json
%LOCALAPPDATA%\PlayRoam\addons\sharpen\sharpen.dll

Select the add-on in Add-ons. PlayRoam marks third-party entries Not verified. They run code inside the app, so install only add-ons you trust. PlayRoam's own bridge DLLs are in components beside the executable.

Use Open add-ons folder to find the user folder. If a manifest is invalid, the app lists the error and won't let you turn the add-on on. Duplicate IDs also prevent activation.

Put DLL dependencies in the add-on's folder or Windows System32. PlayRoam loads the absolute DLL path with LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32. It doesn't search the working directory or PATH. Your selected add-on and Neural Rendering look are saved in %LOCALAPPDATA%\PlayRoam\addons.json.

Set up NVIDIA Neural Rendering

You need Pro, Windows and an NVIDIA RTX 50-series graphics card. Provide your own NVIDIA-signed nvngx_dlssnr.dll, build 310.8.0. PlayRoam checks NVIDIA's signature and this SHA-256 file checksum:

E16BCF15E16E13F527491CDF7845B2FE6521A738D8F7C9C721866A8496E1FC8E
  1. Get a copy from a source you trust, with permission to use it. PlayRoam doesn't ship, sell or download NVIDIA's runtime.

  2. Put the file directly in the add-ons folder, outside any subfolder:

    %LOCALAPPDATA%\PlayRoam\addons\nvngx_dlssnr.dll
    
  3. Select Neural Rendering in Add-ons. Read its status and resolve any error before playing.

PlayRoam rejects other builds and unsigned files. Neural Rendering processes SDR pictures. It doesn't provide DLSS Super Resolution or frame generation. Pro includes the add-on integration, so you don't pay PlayRoam separately for it.

Build an add-on with API 1

Add-on API 1 is the public interface your DLL uses to receive and process pictures. Build a Windows x64 DLL that exports the four functions below using the C ABI. This calling convention lets PlayRoam call those functions by their exact names, without name mangling.

Write the manifest

Create addon.json beside the DLL:

{
  "api": 1,
  "id": "com.example.sharpen",
  "name": "Sharpen",
  "version": "1.0.0",
  "author": "Example",
  "description": "Sharpens the picture.",
  "library": "sharpen.dll"
}
  • api must be the supported API number, 1.
  • id must be a stable, unique identifier matching ^[a-z0-9][a-z0-9._-]{2,63}$ (3 to 64 characters). playroam.neural-rendering is reserved for the built-in add-on.
  • name is the name shown in Add-ons and must not be blank.
  • version is the add-on's version and must not be blank.
  • author names the add-on's author and must not be blank.
  • description describes the picture effect in plain text.
  • library must name the x64 DLL in the same folder. It can't point to another folder. The file must exist and resolve inside the add-on's folder.

Every field is required. A manifest can be at most 64 KiB. PlayRoam rejects unrecognized fields.

Header and exports

Define PLAYROAM_ADDON_BUILD before including the public header when you build the DLL. The complete header is below:

#ifndef PLAYROAM_ADDON_H
#define PLAYROAM_ADDON_H

#include <stdint.h>
#include <d3d11.h>

#define PLAYROAM_ADDON_API 1
#if defined(PLAYROAM_ADDON_BUILD)
#define PLAYROAM_ADDON_EXPORT __declspec(dllexport)
#else
#define PLAYROAM_ADDON_EXPORT
#endif

#ifdef __cplusplus
extern "C" {
#endif

typedef struct PlayroamAddonPicture {
    uint32_t width;
    uint32_t height;
    uint32_t format; /* DXGI_FORMAT_R8G8B8A8_UNORM in API 1 */
    uint32_t fps;
} PlayroamAddonPicture;

typedef struct PlayroamAddonFrame {
    ID3D11DeviceContext* context; /* PlayRoam's immediate context: record GPU work, never wait on the GPU */
    ID3D11Texture2D* input; /* read only */
    ID3D11ShaderResourceView* input_view;
    ID3D11Texture2D* output; /* same size and format; write every pixel */
    ID3D11UnorderedAccessView* output_uav;
    ID3D11RenderTargetView* output_rtv;
    uint64_t index; /* real frames since create, from 0 */
    double time; /* seconds since create */
} PlayroamAddonFrame;

PLAYROAM_ADDON_EXPORT uint32_t playroam_addon_api(void);
PLAYROAM_ADDON_EXPORT int32_t playroam_addon_create(ID3D11Device* device, const PlayroamAddonPicture* picture, void** addon, char* error, uint32_t error_size);
PLAYROAM_ADDON_EXPORT int32_t playroam_addon_process(void* addon, const PlayroamAddonFrame* frame, char* error, uint32_t error_size);
PLAYROAM_ADDON_EXPORT void playroam_addon_destroy(void* addon);

#ifdef __cplusplus
}
#endif
#endif

Export all four functions from the x64 DLL with these exact, undecorated names. PlayRoam checks for them before calling create. playroam_addon_api must return PLAYROAM_ADDON_API, which is 1.

Initialize *addon to null. On success, write your instance pointer there; process and destroy receive that same pointer. Clean up partial allocations if creation fails. If you return a non-null instance on failure, PlayRoam calls destroy on it.

Lifecycle and threading

PlayRoam calls every function on its render thread:

  1. create runs when a stream starts with the add-on enabled, or when you enable it during a stream. If the picture size changes, PlayRoam destroys the old instance and calls create again.
  2. process runs once per real source frame, before frame generation and upscaling. The processed picture is used for display and frame generation. index starts at zero after create. time counts seconds since create.
  3. destroy runs when the stream ends, the add-on is turned off or replaced, the picture layout changes, or the add-on fails. Release your instance and the resources it owns without throwing or waiting for the GPU. GPU work you've already recorded must stay valid under D3D11 resource lifetime rules.

The D3D11 immediate context belongs to PlayRoam. Record GPU work and return without waiting for the GPU. Use this context only during the call.

Picture and frame pointers are borrowed for that call. Don't retain the structure pointers or release borrowed D3D references. Use AddRef/Release or a COM smart pointer for any interface your instance retains.

Restore any pipeline state you change. Unbind input and output views after dispatch or drawing. Never modify input. Write every pixel of output, including alpha. Read through input_view, and write through output_uav or output_rtv. Input and output have the same size and format. Don't assume output still contains a previous frame.

Picture format and HDR

API 1 supplies source-size SDR RGBA8 in DXGI_FORMAT_R8G8B8A8_UNORM, with one mip and one array slice. Output has shader-resource, unordered-access and render-target bindings. picture.fps is the real source frame rate, rounded to the nearest integer. Generated display frames are not new input.

Add-ons don't run on HDR pictures. The stream continues without the add-on, and Add-ons explains that it needs an SDR signal.

After process returns, PlayRoam copies output to its rotating presentation texture. Work recorded on the immediate context runs before that copy. If you use a private GPU queue, you must arrange completion within this contract. Don't modify input, map or read back pictures, flush, spin or wait on the GPU.

Errors

Return 1 from create and process on success, or 0 on failure. On failure, write a short NUL-terminated UTF-8 message into error. Use at most error_size bytes, including the terminator, and never write past the buffer. Write nothing when error_size is zero. Don't let exceptions cross the C ABI.

A failure turns the add-on off for the current stream. PlayRoam shows your error message in Add-ons, while capture and display continue without the add-on. Turn the add-on off and on to retry. Return a useful error instead of leaving output partly written or reporting success when the effect hasn't run.

Keep processing within the frame interval

Your add-on shares the GPU and render thread with video decoding, frame generation, upscaling and display. A real frame arrives every 33.3 ms at 30 FPS or 16.7 ms at 60 FPS. Those intervals must cover the whole stream, so keep your add-on's work well below them. With 2× output, the gap between displayed frames is half the source interval.

Keep CPU work on the render thread small. Compile shaders and create reusable GPU resources in create. In process, avoid per-frame allocations, copies, CPU readback, blocking I/O, synchronization and GPU waits. Limit the GPU work you record, guard threads outside the picture dimensions and return promptly. Measure at the source resolution and frame rate you support. Doubling both picture dimensions means four times as many pixels to process.

Understand what add-ons can access

Add-ons run native code inside PlayRoam with the user's process privileges. API 1 has no sandbox to restrict them. An add-on can access files or the network, crash the app or steal data. Manifest validation and restricted DLL dependency searches don't prevent that access.

PlayRoam doesn't sign, review or verify third-party DLLs. Not verified warns you about this; it isn't a safety check. Install only add-ons you trust. NVIDIA runtime verification is separate and doesn't verify third-party add-ons.

If you write an add-on, avoid unrelated file, account and network access. Describe what your DLL does before users install it.

Build the invert example

Save the header above as playroam_addon.h, with the three files below beside it. Run build.cmd in that folder from an x64 Visual Studio Developer Command Prompt. The build uses cl.exe, the Windows SDK's D3D11 headers, d3d11.lib and d3dcompiler.lib. You don't need to download shader tools.

The example compiles an 8×8 compute shader during creation. It inverts RGB while preserving alpha, handles edge threads, restores compute state and releases its resources during destruction.

invert.cpp:

#define PLAYROAM_ADDON_BUILD
#include "playroam_addon.h"
#include <d3dcompiler.h>
#include <wrl/client.h>
#include <cstring>
#include <new>

using Microsoft::WRL::ComPtr;

struct Invert {
    ComPtr<ID3D11ComputeShader> shader;
    uint32_t width, height;
};

static int32_t fail(char* error, uint32_t size, const char* message) {
    if (error && size) {
        const size_t count = strlen(message) < size - 1 ? strlen(message) : size - 1;
        memcpy(error, message, count);
        error[count] = '\0';
    }
    return 0;
}

uint32_t playroam_addon_api(void) { return PLAYROAM_ADDON_API; }

int32_t playroam_addon_create(ID3D11Device* device, const PlayroamAddonPicture* picture,
                              void** addon, char* error, uint32_t error_size) {
    if (!addon) return fail(error, error_size, "Missing output instance");
    *addon = nullptr;
    if (!device || !picture || picture->format != DXGI_FORMAT_R8G8B8A8_UNORM || !picture->width || !picture->height)
        return fail(error, error_size, "Invert needs RGBA8 pictures");
    static const char source[] =
        "Texture2D<float4> src : register(t0);"
        "RWTexture2D<float4> dst : register(u0);"
        "[numthreads(8,8,1)] void main(uint3 id : SV_DispatchThreadID) {"
        "uint w,h; dst.GetDimensions(w,h); if(id.x>=w || id.y>=h) return;"
        "float4 c=src.Load(int3(id.xy,0)); dst[id.xy]=float4(1.0-c.rgb,c.a); }";
    ComPtr<ID3DBlob> code, messages;
    HRESULT result = D3DCompile(source, sizeof(source) - 1, "invert", nullptr, nullptr,
        "main", "cs_5_0", D3DCOMPILE_OPTIMIZATION_LEVEL3, 0, &code, &messages);
    if (FAILED(result)) return fail(error, error_size, "Can't compile the invert shader");
    Invert* instance = new (std::nothrow) Invert;
    if (!instance) return fail(error, error_size, "Out of memory");
    result = device->CreateComputeShader(code->GetBufferPointer(), code->GetBufferSize(), nullptr, &instance->shader);
    if (FAILED(result)) { delete instance; return fail(error, error_size, "Can't create the invert shader"); }
    instance->width = picture->width;
    instance->height = picture->height;
    *addon = instance;
    return 1;
}

int32_t playroam_addon_process(void* addon, const PlayroamAddonFrame* frame, char* error, uint32_t error_size) {
    if (!addon || !frame || !frame->context || !frame->input_view || !frame->output_uav)
        return fail(error, error_size, "Missing invert frame resources");
    Invert* instance = static_cast<Invert*>(addon);
    // Keep PlayRoam's compute state, including shader class instances.
    ComPtr<ID3D11ComputeShader> old_shader;
    ID3D11ClassInstance* classes[256] = {};
    UINT class_count = 256;
    ComPtr<ID3D11ShaderResourceView> old_srv;
    ComPtr<ID3D11UnorderedAccessView> old_uav;
    frame->context->CSGetShader(&old_shader, classes, &class_count);
    frame->context->CSGetShaderResources(0, 1, &old_srv);
    frame->context->CSGetUnorderedAccessViews(0, 1, &old_uav);
    ID3D11ShaderResourceView* input = frame->input_view;
    ID3D11UnorderedAccessView* output = frame->output_uav;
    frame->context->CSSetShader(instance->shader.Get(), nullptr, 0);
    frame->context->CSSetShaderResources(0, 1, &input);
    frame->context->CSSetUnorderedAccessViews(0, 1, &output, nullptr);
    frame->context->Dispatch((instance->width + 7) / 8, (instance->height + 7) / 8, 1);
    // Unbind before PlayRoam copies the result into its frame.
    ID3D11ShaderResourceView* empty_srv = nullptr;
    ID3D11UnorderedAccessView* empty_uav = nullptr;
    frame->context->CSSetShaderResources(0, 1, &empty_srv);
    frame->context->CSSetUnorderedAccessViews(0, 1, &empty_uav, nullptr);
    input = old_srv.Get();
    output = old_uav.Get();
    frame->context->CSSetShaderResources(0, 1, &input);
    frame->context->CSSetUnorderedAccessViews(0, 1, &output, nullptr);
    frame->context->CSSetShader(old_shader.Get(), classes, class_count);
    for (UINT i = 0; i < class_count; ++i) if (classes[i]) classes[i]->Release();
    return 1;
}

void playroam_addon_destroy(void* addon) { delete static_cast<Invert*>(addon); }

addon.json:

{
  "api": 1,
  "id": "com.example.invert",
  "name": "Invert",
  "version": "1.0.0",
  "author": "PlayRoam SDK example",
  "description": "Inverts picture colours while preserving alpha.",
  "library": "invert.dll"
}

build.cmd:

@echo off
cl.exe /nologo /LD /O2 /EHsc /std:c++17 /W4 invert.cpp /link /OUT:invert.dll d3d11.lib d3dcompiler.lib

Copy invert.dll and addon.json into %LOCALAPPDATA%\PlayRoam\addons\invert. With Pro and an SDR stream, turn on Invert in Add-ons. This example doesn't need an NVIDIA runtime.

Questions and answers

Which plan supports add-ons?

A monthly Pro subscription includes add-ons on Windows. You need an active Pro subscription to turn them on or run them. Android doesn't support add-ons.

Does PlayRoam include NVIDIA's Neural Rendering runtime?

No. You must provide your own NVIDIA-signed nvngx_dlssnr.dll, exact build 310.8.0. Neural Rendering also needs an NVIDIA RTX 50-series graphics card. PlayRoam doesn't ship or sell the runtime.

Do I need an add-on for 2× AI frame generation?

No. 2× AI uses RIFE, included in the PlayRoam installer. Choose it under FPS boost rather than downloading an add-on.

Can I build my own add-on?

Yes. Add-on API 1 accepts a Windows x64 DLL with an addon.json manifest. Add-ons run native code inside PlayRoam. The app marks third-party add-ons Not verified, so install only code you trust.

Try PlayRoam on your screen

Play your PS5 or PS4 at home free on Windows or Android. Pro makes your picture sharper and movement smoother, and adds PS5 play away from home.

Coming soonSee pricing