Create a Mreyti workspace to register domains, get managed SDK snippets and configure webhooks. Read the workspace guide. The standalone SDK below remains available separately.
You own Mreyti: start with steps 1–2 to produce and distribute the SDK. You are adding Mreyti to a website: start at step 3 with the SDK file or URL supplied to you. Customers do not need access to Mreyti’s repository.
01Prepare your SDK
Mreyti is the product name. Its current package is @pykero/eyewear-vto and its HTML element is <eyewear-tryon>. Use these exact names in integrations.
From the Mreyti repository, with the Node version in .node-version:
npm ci
npm run build:lib| Output | Who uses it |
|---|---|
dist-lib/eyewear-vto.iife.js | Customers adding a plain script to a website. JavaScript dependencies are bundled. |
dist-lib/eyewear-vto.js | Bundled applications using the npm SDK; external peer dependencies must resolve. |
dist-lib/types/ | TypeScript declarations for SDK consumers. |
The website build, npm run build, creates dist/ for the landing page and docs. It does not publish an SDK. Pushing main also does not publish a new npm version.
02Publish and host
The existing release path
The repository’s .github/workflows/publish.yml publishes to npm and verifies the published package and script. Prepare a new version in package.json and its lockfile, the exported VERSION in src/widget/embed/index.ts, and CHANGELOG.md. The workflow checks they agree.
Configure NPM_TOKEN as a GitHub Actions secret with permission to publish the package. It belongs only in CI. The browser SDK does not require an API key or a customer token, and this secret must never appear in the script or a customer’s HTML.
Once the changes are reviewed and all release checks pass, push a version tag matching the package version. The workflow publishes the preview distribution tag. A plain push to main is not this release action.
The existing full-runtime size check fails at approximately 7.13 MiB against the 5 MiB cap. No new release is ready until that gate is resolved. Do not bypass it. Physical-fit and realism gates also remain unverified; current integrations are previews.
The script URL customers receive
After a version is successfully published, jsDelivr can serve that version’s IIFE. Replace VERSION below with the version actually published:
https://cdn.jsdelivr.net/npm/@pykero/eyewear-vto@VERSION/dist-lib/eyewear-vto.iife.jsThe previously published 0.1.0 artifact is not automatically updated by new repository commits. Pin a published version for each customer integration.
Hosting the script on your own domain
You can deploy the validated IIFE to a versioned static URL on your own domain and give customers that URL. The current website Docker image serves dist/; it does not automatically expose dist-lib/ or a /sdk/ endpoint. Hosting such an endpoint needs an explicit deployment configuration. Use immutable URLs for released files and a new URL for each release.
A customer may also copy the supplied IIFE to their own website. The complete example below uses /vendor/mreyti/eyewear-vto.iife.js on the customer’s origin. That path must contain the actual SDK file.
03Add it to a customer website
Upload the supplied SDK file to /vendor/mreyti/eyewear-vto.iife.js, or replace the script URL with the versioned URL supplied by Mreyti. Place this element where the mirror should appear:
<eyewear-tryon id="mirror" sku="DEV-48"></eyewear-tryon>
<script defer src="/vendor/mreyti/eyewear-vto.iife.js"></script>Load the script once. Serve the site over HTTPS, or localhost while developing. The element has its own Start button; camera access happens on the shopper’s action. There is no API key, backend endpoint or account registration for the current camera SDK.
For an app using npm, install the package and its peers, then import it in browser code:
npm install @pykero/eyewear-vto@preview three @mediapipe/tasks-visionimport '@pykero/eyewear-vto'Use a pinned, tested version for deployment. See the platform-specific integration notes for CMS editors, iframes and server-rendered applications.
04Configure your SDK instance
Wait until the custom element is defined. Then set the catalogue, choose a SKU and apply settings before Start.
await customElements.whenDefined('eyewear-tryon')
const mirror = document.querySelector('#mirror')
mirror.setFrames([
{ sku: 'MY-48', name: 'Sample narrow', lensWidthMm: 48, bridgeMm: 18,
templeLengthMm: 145, lensHeightMm: 38, rimThicknessMm: 3, frontWidthMm: 128 },
])
mirror.sku = 'MY-48'
mirror.strings = { start: 'Try with Mreyti', capture: 'Photo' }These are example dimensions, not measurements of your products. Without a model URL, the SDK renders a procedural sample. To show a real frame, supply a { spec, modelUrl } entry whose measured dimensions and model satisfy the asset contract. A rejected model falls back to sample geometry. See catalogue setup and model requirements.
| Setting | Use it for |
|---|---|
setFrames([...]) | Register product SKUs, dimensions and optional model URLs. It resets selection, so set the active SKU afterward. |
sku | Match the mirror to the selected product or variant. It must be in your registered catalogue. |
strings | Customize labels and translations without changing the renderer. |
assetUrls | Override both tracker paths before starting, after deploying the corresponding files. |
stop() | Release the camera when closing your modal. Removing the element also cleans up. |
clearCalibration() | Remove the locally stored calibration. |
If you self-host the tracker, deploy the pinned runtime and model, then configure both values:
mirror.assetUrls = {
wasmBase: '/vendor/mediapipe/wasm',
model: '/vendor/mediapipe/face_landmarker.task',
}See the tracker deployment commands. Leaving these settings unset uses the default public asset URLs. Do not put credentials in either URL. Video, landmarks and measurements must remain in the browser.
05Download a complete example
This is a full HTML page with two sample frames, a product selector, custom labels, status messages and camera cleanup. Put it on your website with the SDK file at the documented vendor path. The HTML download does not include the SDK binary.
Read the complete example
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Mreyti SDK — Starter</title>
<style>
body { max-width: 720px; margin: 40px auto; padding: 0 20px; font: 16px/1.6 system-ui, sans-serif; color: #342b23; background: #f6f3eb; }
h1 { font: 42px Georgia, serif; }
.controls { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin: 20px 0; }
select, button { padding: 12px; font: inherit; }
eyewear-tryon { display: block; width: 100%; }
#status { min-height: 2em; }
small { display: block; margin-top: 16px; }
</style>
<!-- Upload the current standalone SDK here, or replace src with the
versioned script URL supplied by Mreyti. Load it only once per page. -->
<script id="mreyti-sdk" defer src="/vendor/mreyti/eyewear-vto.iife.js"></script>
</head>
<body>
<h1>Your Mreyti mirror.</h1>
<p>This starter uses two sample frames. Press the widget’s Start button when ready.</p>
<div class="controls">
<label for="frame">Frame</label>
<select id="frame" disabled>
<option value="MY-48">Sample narrow — 48 mm lenses</option>
<option value="MY-58">Sample wide — 58 mm lenses</option>
</select>
<button id="stop" type="button" disabled>Stop camera</button>
</div>
<p id="status" role="status">Loading Mreyti SDK…</p>
<eyewear-tryon id="mirror"></eyewear-tryon>
<small>Early preview: fit and realism are still being validated. Camera video and measurements stay in this browser.</small>
<script>
const mirror = document.querySelector('#mirror')
const frame = document.querySelector('#frame')
const stop = document.querySelector('#stop')
const status = document.querySelector('#status')
const sdk = document.querySelector('#mreyti-sdk')
sdk.addEventListener('error', () => {
status.textContent = 'SDK could not load. Upload eyewear-vto.iife.js to /vendor/mreyti/ or update the script URL.'
})
customElements.whenDefined('eyewear-tryon').then(() => {
// Example dimensions only. Replace with your measured catalogue.
// Without modelUrl, the SDK makes sample geometry from these dimensions.
mirror.setFrames([
{ sku: 'MY-48', name: 'Sample narrow', lensWidthMm: 48, bridgeMm: 18,
templeLengthMm: 145, lensHeightMm: 38, rimThicknessMm: 3, frontWidthMm: 128 },
{ sku: 'MY-58', name: 'Sample wide', lensWidthMm: 58, bridgeMm: 18,
templeLengthMm: 145, lensHeightMm: 44, rimThicknessMm: 3, frontWidthMm: 148 },
])
// Select AFTER setting the catalogue, which resets the selected index.
mirror.sku = frame.value
mirror.strings = { start: 'Try with Mreyti', nextFrame: 'Next frame', capture: 'Photo' }
// By default, the tracker is downloaded from pinned public URLs on Start.
// To self-host, set BOTH paths before Start after deploying those files:
// mirror.assetUrls = {
// wasmBase: '/vendor/mediapipe/wasm',
// model: '/vendor/mediapipe/face_landmarker.task',
// }
frame.disabled = false
stop.disabled = false
status.textContent = 'Ready. Select a frame, then press Try with Mreyti.'
frame.addEventListener('change', () => { mirror.sku = frame.value })
stop.addEventListener('click', () => {
mirror.stop()
status.textContent = 'Camera stopped. Press Try with Mreyti to restart.'
})
mirror.addEventListener('vto-ready', () => { status.textContent = 'Your mirror is ready.' })
mirror.addEventListener('vto-error', () => {
status.textContent = 'The mirror could not start. Follow the message inside the widget.'
})
mirror.addEventListener('vto-model-rejected', () => {
status.textContent = 'A model failed validation. A sample frame is showing instead.'
})
mirror.addEventListener('vto-frame-changed', () => { frame.value = mirror.sku })
// Never log, upload or forward camera data or measurement event payloads.
})
window.addEventListener('pagehide', () => {
if (typeof mirror.stop === 'function') mirror.stop()
})
</script>
</body>
</html>
06Verify the setup
- The SDK script returns JavaScript with HTTP 200, and the widget’s Start button appears.
- The frame selector switches between the two configured sample SKUs.
- The camera opens only after Start and permission. Stop releases it.
- Reloading requires a new explicit Start; calibrate again for a new camera stream.
- Test your real models, mobile layout and target devices. A working embed does not establish physical fit.
The starter handles a missing script with a setup message. The existing widget handles camera and tracking failures. For more detail, see troubleshooting and the API reference.