Skip to content

Local Development

Once your extension is scaffolded, you don't have to rebuild and re-upload a ZIP on every change. The platform can load your pages directly from your local dev server and surface them on a real Stripchat testing stand — so you keep hot-reload, DevTools, and breakpoints while the extension runs end-to-end inside a real model's room.

This page walks through that loop.

How the Dev Cycle Works

Two dashboard features make this possible:

  • Start URI — a per-version URL that the platform uses instead of the files inside your uploaded ZIP. The pages declared in views.pages are resolved against this URL.
  • Publish to Stand — snapshots the current draft (Start URI + ZIP + manifest) and exposes it on a separate Stripchat testing stand where it can be installed by a model account.

WARNING

The ZIP archive is still required — its manifest.json is the source of truth for slots, pages, actions, and the resolver script. Only the page files themselves are served from your dev server.

INFO

Vite is the officially supported dev server, and the starter template ships pre-configured for it. Any other tool that serves static HTML/JS/CSS over HTTP works too — you'll just have to translate the Vite-specific snippets below to its equivalents.

Prerequisites

  • A Stripchat account with the Developer role enabled — see Upload Extension → Become a Developer.
  • A model account you can stream from. The same Stripchat account can hold both Developer and Model roles.
  • A local extension project. The fastest path is to scaffold one using the starter template — it already includes the dev-server config below.

Step 1. Make sure your archive is internally consistent

Before anything is published to the stand, two things must agree inside your manifest.json. Check them once up front — most "extension does not appear" issues come from a mismatch here.

1.1 Every slot maps to a page

views.slots must be within the slots allowed for your extension category, and every entry in views.slots needs a corresponding page in views.pages:

json
{
  "views": {
    "slots": [
            "EXTENSION_SLOT_MAIN_GAME_FUN", 
            "EXTENSION_SLOT_RIGHT_OVERLAY", 
            "EXTENSION_SLOT_BACKGROUND"
        ],
    "pages": {
      "menu": "mainGameFun.html",
      "overlay": "rightOverlay.html",
      "background": "background.html"
    },
    "resolveSlotPageScript": "resolveSlotPage.js"
  }
}

1.2 The resolver script returns a valid page key for each slot

resolveSlotPage.js is required. It lives at the archive root, is not bundled, and is called by the platform for every slot to pick which page key from views.pages to load. If it returns null (or an unknown key) for a slot, that slot does not render — even if everything else is set up correctly.

js
// resolveSlotPage.js
export default function resolveSlotPage(slotType, context, extSettings) {
  switch (slotType) {
    case 'EXTENSION_SLOT_MAIN_GAME_FUN':  
        return 'menu';
    case 'EXTENSION_SLOT_RIGHT_OVERLAY':  
        return 'overlay';
    case 'EXTENSION_SLOT_BACKGROUND':     
        return 'background';
    default:                              
        return null;
  }
}

See Resolve Slot Page for the full slot-type list and context shape.

DANGER

A missing or broken resolveSlotPage.js is the single most common reason an extension publishes successfully but never appears in the model's room. Verify it before debugging anything else.

Step 2. Configure the dev server

The platform loads your pages inside an iframe served from the testing-stand origin (not the main Stripchat site). Your dev server has to accept those cross-origin requests. For Vite:

ts
// vite.config.ts
import { defineConfig } from 'vite';

export default defineConfig({
  base: './',
  server: {
    cors: true,
    allowedHosts: true,
  },
});
  • cors: true — adds permissive CORS headers so the iframe can load assets.
  • allowedHosts: true — disables Vite's host check, which would otherwise reject the proxied hostname.

INFO

The starter template already includes both options. Add them manually only if you bootstrapped the project yourself.

Start the server and note the URL it prints (e.g. http://localhost:5173).

Step 3. Create a draft on the dashboard

Follow Upload Extension → Step 2 to create the version. For the local-dev loop, the minimum required tabs are:

  • Version Details — must be filled in before the Start URI can be saved.
  • Files — upload an initial ZIP archive with a valid manifest.json and resolveSlotPage.js at the root. The HTML pages inside the archive can be stubs; during local development the platform serves them from your Start URI. See Archive Structure for the required layout.

The Image Assets and Restrictions tabs only need to be filled in before you submit for review.

The Files tab after a successful upload ("Build ready" appears under the archive):

Files tab with the build-ready archive

Step 4. Set the Start URI

Open the For Testing tab of your draft version, paste the dev server URL into the Start URI field, and save. Whitespace is trimmed automatically.

The platform combines this URI with the page paths from views.pages. With the manifest from Step 1 and a Start URI of http://localhost:5173, the resolver returning 'menu' for EXTENSION_SLOT_MAIN_GAME_FUN causes the platform to load http://localhost:5173/mainGameFun.html.

WARNING

Start URI is draft-only state — it is used only when the draft is published to the stand and has no effect on production builds.

The For Testing tab with the Start URI saved (still in Draft):

For Testing tab with Start URI = http://localhost:5173

Step 5. Publish to Stand

Click Publish to Stand. This snapshots the current Start URI + ZIP + manifest and exposes them on the testing stand. A success modal links you to the stand.

"Published to Stand" success modal with View Stand button

TIP

Open the stand link in a browser profile separate from the dashboard so you can stay logged in as a model on one side and a viewer on the other.

Step 6. Install and test as a model

After publishing, the version switches to the Testing state and a View Stand button appears next to Publish to Stand:

Version in Testing state with View Stand button

  1. Open the testing stand (link from the success modal, or the View Stand button on the version).
  2. Log in with your model account.
  3. Find your extension in the library and install it.
  4. Open the extension's settings on the model profile and enable it.
  5. Start a stream from the model account.
  6. As a viewer (a different browser profile, incognito window, or a separate viewer account), open the model's room and interact with the extension.

Since the iframe loads from your localhost, you can keep DevTools attached: breakpoints, console, network, and HMR all behave normally.

Step 7. Iterate

Different changes require different actions to take effect on the stand:

ChangeAction
Page code (.ts, .tsx, .css, HTML, assets served by the dev server)None — HMR. Refresh the slot iframe if needed.
manifest.jsonRe-build → re-upload ZIP → Publish to Stand.
resolveSlotPage.jsRe-build → re-upload ZIP → Publish to Stand.
icon.svg (used as the moveableOverlay collapsed-state visual, and across the platform)Re-upload ZIP → Publish to Stand, then reload the model's stream page. Not served from the dev server.
Start URI (e.g. dev server moved to a new port)Save Start URI → Publish to Stand.
Settings form / extension settings.htmlRe-build → re-upload ZIP → Publish to Stand, then re-open settings on the model account.

WARNING

Re-uploading the ZIP or changing the Start URI does not automatically publish to the stand. Click Publish to Stand again, then refresh the model's stream page (or restart the stream) to load the new snapshot.

Browser Setup

The dev-loop iframe loads http://localhost from the testing-stand page. In Chromium 142+ (Chrome) this requires a one-time permission on the stand origin — without it the iframe stays blank and the console prints Permission was denied for this request to access the ... address space.

  1. Firefox — works out of the box. The fastest fallback if you don't want to configure Chrome.
  2. Chrome — allow access in 5 steps:
    1. Open the View Stand link from Step 5 and note the hostname (<stand-origin>).
    2. Go to chrome://settings/content/siteDetails?site=https://<stand-origin>.
    3. Set Apps on device (loopback-network) to Allow.
      • On Chromium 142–144 the same setting is named Local Network Access (local-network-access).
      • If your dev server runs on a LAN IP (e.g. http://192.168.1.10:5173) instead of localhost, also set Local Network (local-network) to Allow. Stick to localhost to avoid this.
    4. Reload the stand page.

Pre-flight Checklist

Run through this before your first Publish to Stand, and any time the extension stops appearing:

  • [ ] Every slot in views.slots has an entry in views.pages.
  • [ ] resolveSlotPage.js is at the root of the ZIP (not inside assets/) and is referenced by views.resolveSlotPageScript.
  • [ ] The resolver returns a valid views.pages key — not null — for every slot in views.slots.
  • [ ] vite.config.ts has server.cors: true and server.allowedHosts: true (or the equivalent for your dev server).
  • [ ] The dev server is running and reachable at the saved Start URI.
  • [ ] Version Details and Files tabs are filled in.

Troubleshooting

"Please complete Version Details before setting a Start URI." The Version Details tab is empty. Fill in summary and description, then return to the For Testing tab.

"Please upload extension assets in the Files tab before publishing." A valid ZIP must exist before the first Publish to Stand. Upload an archive in the Files tab and retry.

The extension does not appear in the model's room at all. Walk the pre-flight checklist. Pay particular attention to resolveSlotPage.js — see Resolve Slot Page.

The extension shows up but pages are blank or 404. Verify the Start URI matches the running dev server (correct host, port, scheme), and that page paths in views.pages resolve relative to that URI. Open DevTools → Network tab inside the slot iframe to see what failed.

Vite logs Blocked request. This host is not allowed. Add allowedHosts: true to server in vite.config.ts and restart the dev server. (For non-Vite tooling, disable the equivalent host check.)

CORS errors in DevTools. Add cors: true to server in vite.config.ts and restart the dev server. (For non-Vite tooling, enable permissive CORS headers on the dev server.)

Iframe is blank, console says Permission was denied for this request to access the ... address space (or shows the LNA prompt). Chromium 142+ is blocking the loopback request from the testing-stand origin until Local Network Access is granted. See Browser Setup: allow Apps on device (loopback-network) for https://<stand-origin> in Chrome/Edge, drop Brave Shields, or switch from Safari to Chrome/Firefox.

Settings changes don't show up for the model. The settings form is part of the ZIP, not the Start URI. Re-build, re-upload the ZIP, Publish to Stand, and re-open the extension settings on the model account.