Live playground
Every control below edits the real element on the left. The snippet updates as you go, and the event log shows exactly what your page would receive.
face_landmarker.task.
Empty = Google's public CDN. For a live store, host both files on your own
domain so the try-on never depends on a third party:
copy node_modules/@mediapipe/tasks-vision/wasm and download the
.task file into your static folder, then fill in both
fields — one without the other quietly mixes your runtime with a CDN model.
<eyewear-studio>: the shopper uploads
a photo and gets it back as a clean studio shot (neutral backdrop, even light,
frames untouched). It never sees the camera or the try-on — only the file the
shopper chose leaves the page. The endpoint is a URL on your server that
holds the image-service key and forwards the request; drop
tools/studio-proxy.mjs (one function, no dependencies) into your
backend. Here it points at this playground's own built-in copy. Empty = the
element is not rendered at all.
The element is plain HTML with a script tag, so it goes wherever your theme lets you put markup. Three places that usually means.
<script type="module" src="https://your.cdn/eyewear-vto.js"></script>
<eyewear-tryon sku="ACME-52"></eyewear-tryon>
That is the whole integration. The element renders a start button and stays inert until it is pressed, so it costs the page essentially nothing until a shopper uses it.
<!-- sections/product-tryon.liquid -->
<script type="module" src="{{ 'eyewear-vto.js' | asset_url }}"></script>
<eyewear-tryon sku="{{ product.selected_or_first_available_variant.sku }}">
</eyewear-tryon>
{% schema %}
{ "name": "Virtual try-on", "target": "section" }
{% endschema %}
Upload the bundle as a theme asset rather than
hotlinking a CDN — Shopify serves assets from its own domain, so you avoid a
third-party request on every product page. Re-render the element's
sku when the variant picker changes; the widget swaps frames without
restarting the camera.
// functions.php
add_action( 'woocommerce_single_product_summary', function () {
global $product;
wp_enqueue_script_module(
'eyewear-vto',
get_stylesheet_directory_uri() . '/js/eyewear-vto.js'
);
printf(
'<eyewear-tryon sku="%s"></eyewear-tryon>',
esc_attr( $product->get_sku() )
);
}, 35 );
Priority 35 puts it below the add-to-cart button
and above the meta block. wp_enqueue_script_module needs WordPress
6.5+; before that, print the <script type="module"> tag yourself —
wp_enqueue_script cannot emit one.