Home / Collections / Fusions xl
Fusions XL Safety Glasses
5 Items

Filter

Clear all
Sort by:  Featured
Product Type
Colour
Lens Type
Price
$0.00
$69.99
Larger Fit
$69.99 AUD

4.8 (37)

Larger Fit
$69.99 AUD

4.8 (37)

Larger Fit
$69.99 AUD

4.8 (37)

Larger Fit
$39.99 AUD

4.8 (37)

Larger Fit
$29.99 AUD

4.8 (37)

// Drop-in replacement: renders camera video INSIDE the same WebGL canvas as the 3D model (sticky composite). // Fixes: background quad writing depth (which would hide the model). // Notes: // - Keeps your UI/HTML/CSS and product logic basically identical. // - Uses MediaPipe Tasks Vision 0.10.32 with NAMED exports (no default export). // - Turns off blendshapes (you weren't using them) for free perf. // - Avoids resizing canvas every frame; only resizes when needed. import { FaceLandmarker, FilesetResolver } from "https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.32"; import * as THREE from "https://cdn.skypack.dev/three@0.129.0/build/three.module.js"; import { GLTFLoader } from "https://cdn.skypack.dev/three@0.129.0/examples/jsm/loaders/GLTFLoader.js"; import { DRACOLoader } from "https://cdn.skypack.dev/three@0.129.0/examples/jsm/loaders//DRACOLoader.js"; import { RoomEnvironment } from "https://cdn.skypack.dev/three@0.129.0/examples/jsm/environments/RoomEnvironment.js"; class SSTO { VIDEO_DEPTH = 500; FOV_DEGREES = 63; NEAR = 1; FAR = 10000; video = null; lastVideoTime = -1; constructor(product_index, ssto_config) { console.log("constructing"); this.product_index = product_index; this.ssto_config = ssto_config; const self = this; const css = `body.noscroll { overflow:hidden!important;position:fixed!important;} * {border-collapse: collapse; } .ssvto-container { z-index: 1000001; position: fixed; top: 0; left: 0;width: 100%;height: 100%;background-color: rgba(0, 0, 0, 0.5);display: flex;justify-content: center;align-items: center; } .ssvto-main { margin-left: 20px;margin-right: 20px;border-radius: 10px; align-items: start;position: relative;max-width: 1200px; max-height: 700px; height: 700px; background-color: white; display: flex; justify-content: start; flex-direction: row; } .ssvto-main.ssvto-accept { flex-direction: column; width: 100%; align-items: center; align-content: center; justify-content: center; } .ssvto-main.ssvto-app-init { flex-direction: column; width: 100%; align-items: center; align-content: center; justify-content: center; } .ssvto-accept-message { max-width: 700px; padding-top: 20px; padding-bottom: 20px; font-size: 16px; font-family: "chivo"; font-weight: 900; text-align: center; text-transform: uppercase; font-style: italic; } .ssvto-shield { width: 30px; } .ssvto-close-icon { z-index: 1000001;position: absolute; top: 14px; left: 13px; width: 12px; cursor: pointer; filter: invert(1);} .ssvto-range-title { text-transform: uppercase; font-style: italic; font-family: "swizzy"; font-weight: 900; text-align: left; padding-left: 40px; padding-top: 7px; padding-bottom: 8px; border-bottom: 2px solid #eee; } .ssvto-left { position: relative; width: 50%; display: flex; flex-direction: column; max-height: 700px; padding-bottom: 25px; } .ssvto-products { overflow-y: auto; width: 100%; } .ssvto-products-inner { display: flex; justify-content: start; align-items: start; flex-wrap: wrap; gap: 15px; padding: 20px 40px 20px 40px; } .ssvto-product { display: flex; min-width: 130px; flex: 1 1 0; max-width: 33%; flex-direction: column; align-items: center;} .ssvto-product img { border: 2px solid #eee; width: 100%; } .ssvto-product.selected img { border: 2px solid #000; } .ssvto-product-title { text-transform: uppercase;font-size: 12px;font-style: italic;font-family: "swizzy";font-weight: 900;text-align: center; padding: 5px; } .ssvto-video-container { position: relative; width: 50%; height: 100%; background-color: black; overflow: hidden; } .ssvto-video { width:100%; height:100%; object-fit: cover; } .ssvto-flip { transform: scaleX(-1); } .ssvto-canvas { height:100%; object-fit: cover; top: 0px; left: 0px; position: absolute; } .ssvto-mobile-product-prev,.ssvto-mobile-product-next { display: none; } .ssvto-selected-product-title { max-width: 220px; text-align: center; position: absolute; display: block; color: white; left: 50%; bottom: 50px; transform: translateX(-50%);letter-spacing: 2px; font-size: 14px; font-style: italic; font-family: "swizzy"; font-weight: 900; text-transform: uppercase;} .ssvto-add-to-cart-button { position: absolute; bottom: 10px; left: 50%; transform: translateX(-50%); } .ssvto-loader { display: none; background: rgba(0, 0, 0, 0.5);padding: 20px;border-radius: 10px;position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);z-index: 20000;width: 150px;height: 150px; } .ssvto-accept-button-set { display: flex; justify-content: center; align-items: center; gap: 15px; } .ssvto-button { border: 1px solid #000; background-color: #fff; color: #000; padding: 7px 15px; border-radius: 0px; font-size: 13px; text-transform: uppercase; font-family: "swizzy"; font-weight: 900; cursor: pointer; font-style: italic; } .ssvto-button.black { background-color: #000; color: #fff; } #ssvto-controls-button { width: 10px; height: 10px;position: absolute; top: 0px; right: 0px; background-color: #00000000; color: #fff; border: 0px solid #fff; padding: 5px; border-radius: 5px; cursor: pointer; } #ssvto-controls { position: absolute; top: 10px; right: 10px; display: none; flex-direction: column; gap: 5px; } #ssvto-controls input { padding: 5px; border: 1px solid #000; background: #fff; width: 75px; } .ssvvt-control-set { display: flex; gap: 5px; } .ssvvt-control-set button { width: 40px; text-align: center;font-size: 10px; font-family: swizzy; font-weight: 900; font-style: italic;text-transform: uppercase;border-radius: 5px; font-size: 12px; padding: 3px; border: 1px solid #fff; background-color: #000; color: #fff; cursor: pointer; } .ssvvt-control-set label {text-shadow: 1px 1px #fff; align-content: center; width: 100px; text-align: right; font-family: swizzy; font-weight: 900; text-transform: uppercase; font-style: italic; font-size: 12px; } @media screen and (orientation: portrait) { .ssvto-close-icon { filter: invert(0); width: 25px; top: 15px; left: 15px;} .ssvto-main { margin-left: 0px;margin-right: 0px;max-height: 100%; max-width: 100%; height: 100%; display: block; border-radius: 0px; width: 100%;} .ssvto-left { padding-bottom: 35px;position: absolute; bottom: 0px; width: 100%; height: 230px; z-index: 10;} .ssvto-video-container { width: 100%; } .ssvto-range-title { display: none; } .ssvto-products { width: 100%; overflow-y: hidden; } .ssvto-products-inner { transition: left 0.6s;display: flex; justify-content: start; align-items: center; flex-wrap: nowrap; gap: 22px; padding: 10px 35px 10px 35px; position: absolute; } .ssvto-product { max-width: 75px;min-width: 75px; width: 75px; transition: width 0.6s, max-width 0.6s, padding-bottom 0.6s; } .ssvto-product.selected { width: 130px; max-width: 130px; padding-bottom: 0px; } .ssvto-product img { border-width: 0px; border-radius: 10px; } .ssvto-product.selected img { border: 0px; } .ssvto-product-title { color: #fff; letter-spacing: 2px;} .ssvto-product .ssvto-product-title { display: none; } .ssvto-selected-product-title { max-width: 220px; text-align: center; position: absolute; display: block; color: white; left: 50%; bottom: 50px; transform: translateX(-50%);letter-spacing: 2px; font-size: 11px; font-style: italic; font-family: "chivo"; font-weight: 900; text-transform: uppercase;} .ssvto-add-to-cart-button { z-index: 1000; position: absolute; bottom: 7px; left: 50%; transform: translateX(-50%); } .ssvto-mobile-product-prev { z-index: 1000;display: block; width: 30px; height: 30px; position: absolute; bottom: 40px; left: 20px; cursor: pointer; } .ssvto-mobile-product-next { z-index: 1000;display: block; width: 30px; height: 30px; position: absolute; bottom: 40px; right: 20px; cursor: pointer; } .ssvto-accept-message { padding-left: 20px; padding-right: 20px; } .ssvto-app-init { padding: 20px; } }`; document.head.insertAdjacentHTML("beforeend", ``); const html = ` `; const dialog = document.createElement("div"); dialog.className = "ssvto-container"; dialog.innerHTML = html; document.body.appendChild(dialog); document.body.classList.add("noscroll"); document.querySelector(".ssvto-close-icon").onclick = function () { if (self.webcamRunning) self.webcamRunning = false; if (self.stream) self.stream.getTracks().forEach((track) => track.stop()); document.body.removeChild(dialog); document.body.classList.remove("noscroll"); }; this.video = document.querySelector(".ssvto-video"); this.canvas_element = document.querySelector(".ssvto-canvas"); this.loading = document.querySelector(".ssvto-loader"); for (let i = 0; i < ssto_config.products.length; i++) { const product = ssto_config.products[i]; const product_element = document.createElement("div"); product_element.className = "ssvto-product"; if (i === product_index) { product_element.classList.add("selected"); document.querySelector(".ssvto-selected-product-title").innerHTML = product.title; } product_element.innerHTML = `
${product.title}
`; product_element.addEventListener("click", () => { this.load_new_product(product_element); }); document.querySelector(".ssvto-products-inner").appendChild(product_element); } document.querySelector(".ssvto-mobile-product-prev").addEventListener("click", () => { this.prev(); }); document.querySelector(".ssvto-mobile-product-next").addEventListener("click", () => { this.next(); }); [...document.querySelectorAll(".ssvto-add-to-cart-button")].forEach((el) => el.addEventListener("click", async () => { const selected_product = document.querySelector(".ssvto-product.selected"); this.product_index = Array.from(selected_product.parentElement.children).indexOf(selected_product); const product_data = ssto_config.products[product_index]; if (ssto_config.on_add_to_cart) { this.loading.style.display = "flex"; await ssto_config.on_add_to_cart(product_data); this.loading.style.display = "none"; } }) ); window.addEventListener("resize", () => { this.handleResize(); this.adjustProductView(); }); window.addEventListener("orientationchange", () => { this.handleResize(); this.adjustProductView(); }); document.getElementById("ssvto-accept-button").onclick = function () { self.set_cookie("ssto_accept", "yes"); document.querySelector(".ssvto-accept").style.display = "none"; document.querySelector(".ssvto-app-init").style.display = "flex"; self.run_ssvto(); window.ss_ab?.("signal", { signal: "virtual-try-on-terms-accepted" }); }; document.getElementById("ssvto-decline-button").onclick = function () { self.set_cookie("ssto_accept", "no"); document.body.removeChild(dialog); document.body.classList.remove("noscroll"); window.ss_ab?.("signal", { signal: "virtual-try-on-terms-declined" }); }; document.getElementById("ssvto-init-cancel-button").onclick = function () { if (self.webcamRunning) self.webcamRunning = false; if (self.stream) self.stream.getTracks().forEach((track) => track.stop()); document.body.removeChild(dialog); document.body.classList.remove("noscroll"); }; if (this.get_cookie("ssto_accept") === "yes") { document.querySelector(".ssvto-accept").style.display = "none"; document.querySelector(".ssvto-app-init").style.display = "flex"; this.run_ssvto(); } else { document.querySelector(".ssvto-accept").style.display = "flex"; document.querySelector(".ssvto-app").style.display = "none"; } // touch events this.touchstartX = 0; this.touchendX = 0; this.canvas_element.addEventListener("touchstart", (e) => { this.touchstartX = e.changedTouches[0].screenX; }); this.canvas_element.addEventListener("touchend", (e) => { this.touchendX = e.changedTouches[0].screenX; this.checkDirection(e); }); // controls (unchanged) document.getElementById("sstvto-control-x-shift").addEventListener("change", (e) => { if (this.model_scene) this.model_scene.position.x = parseFloat(e.target.value); }); document.getElementById("sstvto-control-y-shift").addEventListener("change", (e) => { if (this.model_scene) this.model_scene.position.y = parseFloat(e.target.value); }); document.getElementById("sstvto-control-z-shift").addEventListener("change", (e) => { if (this.model_scene) this.model_scene.position.z = parseFloat(e.target.value); }); document.getElementById("sstvto-control-x-tilt").addEventListener("change", (e) => { if (this.model_scene) this.model_scene.rotation.x = parseFloat(e.target.value); }); document.getElementById("sstvto-control-scale").addEventListener("change", (e) => { if (this.model_scene) { const s = parseFloat(e.target.value); this.model_scene.scale.set(s, s, s); } }); const btn = (id, fn) => document.getElementById(id).addEventListener("click", fn); btn("sstvto-control-x-shift-plus-one", () => this._bumpPos("x", 1, "sstvto-control-x-shift")); btn("sstvto-control-x-shift-plus-point-one", () => this._bumpPos("x", 0.1, "sstvto-control-x-shift")); btn("sstvto-control-x-shift-minus-one", () => this._bumpPos("x", -1, "sstvto-control-x-shift")); btn("sstvto-control-x-shift-minus-point-one", () => this._bumpPos("x", -0.1, "sstvto-control-x-shift")); btn("sstvto-control-y-shift-plus-one", () => this._bumpPos("y", 1, "sstvto-control-y-shift")); btn("sstvto-control-y-shift-plus-point-one", () => this._bumpPos("y", 0.1, "sstvto-control-y-shift")); btn("sstvto-control-y-shift-minus-one", () => this._bumpPos("y", -1, "sstvto-control-y-shift")); btn("sstvto-control-y-shift-minus-point-one", () => this._bumpPos("y", -0.1, "sstvto-control-y-shift")); btn("sstvto-control-z-shift-plus-one", () => this._bumpPos("z", 1, "sstvto-control-z-shift")); btn("sstvto-control-z-shift-plus-point-one", () => this._bumpPos("z", 0.1, "sstvto-control-z-shift")); btn("sstvto-control-z-shift-minus-one", () => this._bumpPos("z", -1, "sstvto-control-z-shift")); btn("sstvto-control-z-shift-minus-point-one", () => this._bumpPos("z", -0.1, "sstvto-control-z-shift")); btn("sstvto-control-x-tilt-plus-one", () => this._bumpRotX(1, "sstvto-control-x-tilt")); btn("sstvto-control-x-tilt-plus-point-one", () => this._bumpRotX(0.1, "sstvto-control-x-tilt")); btn("sstvto-control-x-tilt-minus-one", () => this._bumpRotX(-1, "sstvto-control-x-tilt")); btn("sstvto-control-x-tilt-minus-point-one", () => this._bumpRotX(-0.1, "sstvto-control-x-tilt")); btn("sstvto-control-scale-plus-one", () => this._bumpScale(1, "sstvto-control-scale")); btn("sstvto-control-scale-plus-point-one", () => this._bumpScale(0.1, "sstvto-control-scale")); btn("sstvto-control-scale-minus-one", () => this._bumpScale(-1, "sstvto-control-scale")); btn("sstvto-control-scale-minus-point-one", () => this._bumpScale(-0.1, "sstvto-control-scale")); document.getElementById("ssvto-controls-button").addEventListener("click", () => { document.getElementById("ssvto-controls").style.display = "flex"; document.getElementById("ssvto-controls-button").style.display = "none"; }); document.getElementById("ssvvt-control-save-button").addEventListener("click", async () => { const model = this.ssto_config.products[this.product_index]; const adjustments = { x_shift: document.getElementById("sstvto-control-x-shift").value, y_shift: document.getElementById("sstvto-control-y-shift").value, z_shift: document.getElementById("sstvto-control-z-shift").value, x_tilt: document.getElementById("sstvto-control-x-tilt").value, scale: document.getElementById("sstvto-control-scale").value, }; model.adjustments_json = JSON.stringify(adjustments); await this.save_adjustments(model); }); } _bumpPos(axis, delta, inputId) { if (!this.model_scene) return; this.model_scene.position[axis] += delta; document.getElementById(inputId).value = this.model_scene.position[axis]; } _bumpRotX(delta, inputId) { if (!this.model_scene) return; this.model_scene.rotation.x += delta; document.getElementById(inputId).value = this.model_scene.rotation.x; } _bumpScale(delta, inputId) { if (!this.model_scene) return; const s = this.model_scene.scale.x + delta; this.model_scene.scale.set(s, s, s); document.getElementById(inputId).value = s; } async save_adjustments(model) { this.loading.style.display = "flex"; const response = await fetch("https://portal.safestyle.com.au/service/ssvto-adjustments", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(model), }); await response.json(); this.loading.style.display = "none"; } run_ssvto() { const self = this; this.createFaceLandmarker().then(() => { if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { this.loading.style.display = "none"; document.querySelector(".ssvto-app-init").innerHTML = '
Unable to access camera in this browser.

'; document.getElementById("ssvto-init-cancel-button").onclick = function () { if (self.webcamRunning) self.webcamRunning = false; if (self.stream) self.stream.getTracks().forEach((track) => track.stop()); const container = document.querySelector(".ssvto-container"); if (container) document.body.removeChild(container); document.body.classList.remove("noscroll"); }; return; } navigator.mediaDevices .getUserMedia({ video: { width: 640 } }) .then((stream) => { this.stream = stream; this.video.srcObject = stream; this.webcamRunning = true; document.querySelector(".ssvto-app-init").style.display = "none"; document.querySelector(".ssvto-app").style.display = "flex"; let started = false; const start_video_pipeline = async () => { if (started) return; started = true; // Hide DOM video; we render it inside WebGL this.video.style.display = "none"; // Create video background once we have dimensions this.initVideoBackground(); // initial sizing this.handleResize(); self.predictWebcam(); this.webcam_running = true; this.check_disable_loader(); }; self.video.addEventListener("loadeddata", start_video_pipeline, { once: true, }); self.video.addEventListener("loadedmetadata", start_video_pipeline, { once: true, }); // Explicit play helps some desktop browsers start stream decoding reliably. this.video.play().catch((error) => { console.error("SSTO video.play() failed", error); }); }) .catch((error) => { console.error("SSTO getUserMedia failed", error); this.loading.style.display = "none"; document.querySelector(".ssvto-app-init").innerHTML = '
Unable to access camera.

Check browser camera permissions and try again.

'; document.getElementById("ssvto-init-cancel-button").onclick = function () { if (self.webcamRunning) self.webcamRunning = false; if (self.stream) self.stream.getTracks().forEach((track) => track.stop()); const container = document.querySelector(".ssvto-container"); if (container) document.body.removeChild(container); document.body.classList.remove("noscroll"); }; }); // keep your original behavior this.webcamRunning = false; this.loader = new GLTFLoader(); this.DRACOLoader = new DRACOLoader(); this.DRACOLoader.setDecoderPath("https://www.gstatic.com/draco/v1/decoders/"); this.loader.setDRACOLoader(this.DRACOLoader); // Foreground scene this.scene = new THREE.Scene(); this.renderer = new THREE.WebGLRenderer({ canvas: this.canvas_element, alpha: false, // camera is in-canvas now antialias: true, }); const environment = new RoomEnvironment(); const pmremGenerator = new THREE.PMREMGenerator(this.renderer); this.renderer.toneMapping = THREE.ACESFilmicToneMapping; this.renderer.toneMappingExposure = 0.5; this.scene.environment = pmremGenerator.fromScene(environment).texture; this.renderer.outputEncoding = THREE.sRGBEncoding; this.renderer.localClippingEnabled = true; this.faceGroup = new THREE.Group(); this.faceGroup.matrixAutoUpdate = false; this.scene.add(this.faceGroup); this.clipPlaneVector = new THREE.Vector3(0, 0, 1); this.clipPlane = new THREE.Plane(this.clipPlaneVector, 84); this.clipPlanes = [this.clipPlane]; const model = this.ssto_config.products[this.product_index]; this.load_model(model); }); } // ----- NEW: WebGL background video compositing ----- // DROP-IN: replace your current initVideoBackground() with this version. // It fixes the “lighter” camera by bypassing tone mapping on the background // and using sRGB encoding for the video texture (Three r129 compatible). initVideoBackground() { this.bgScene = new THREE.Scene(); this.bgCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1); this.videoTexture = new THREE.VideoTexture(this.video); this.videoTexture.minFilter = THREE.LinearFilter; this.videoTexture.magFilter = THREE.LinearFilter; this.videoTexture.generateMipmaps = false; this.videoTexture.wrapS = THREE.ClampToEdgeWrapping; this.videoTexture.wrapT = THREE.ClampToEdgeWrapping; // ✅ helps match DOM
Fusions XL Safety Glasses

The Fusions XL are the ultimate XL safety glasses. Designed for larger heads, the Fusions XL safety glasses are 15% larger than the original Fusions for a wider and more comfortable fit.

My bag

Empty bag

Your bag is currently empty.

Don't forget about your free gift!