{"id":4734,"date":"2025-12-06T14:01:14","date_gmt":"2025-12-06T14:01:14","guid":{"rendered":"https:\/\/mastervincehypnosis.com\/?page_id=4734"},"modified":"2026-04-01T16:27:33","modified_gmt":"2026-04-01T16:27:33","slug":"haptics","status":"publish","type":"page","link":"https:\/\/mastervincehypnosis.com\/?page_id=4734","title":{"rendered":"Haptics"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"4734\" class=\"elementor elementor-4734\">\n\t\t\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-21b1721 elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"21b1721\" data-element_type=\"section\" data-e-type=\"section\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t\t\t\t<div class=\"elementor-background-overlay\"><\/div>\n\t\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-no\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-46ee004\" data-id=\"46ee004\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-1c3a813 elementor-widget elementor-widget-heading\" data-id=\"1c3a813\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h1 class=\"elementor-heading-title elementor-size-default\">Haptics<\/h1>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-3d4c501 elementor-widget elementor-widget-heading\" data-id=\"3d4c501\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h6 class=\"elementor-heading-title elementor-size-default\"><p>Hi everyone! <br> <br>This works only with CHROME.<br> \nWe're going to use a Bluetooth connection with your controller\/device. <br>\nYou need to use <a href=\"https:\/\/intiface.com\/central\" target=\"_blank\" rel=\"noopener\" style=\"text-decoration: underline\">Intiface Central<\/a> on your computer. <br>\nYou'll need an mp3 file and a funscript file, provided in each post. <br>\nSee list of posts and more info <a href=\"https:\/\/www.patreon.com\/posts\/85042659\" target=\"_blank\" rel=\"noopener\" style=\"text-decoration: underline\">on this page<\/a>. <br><br>\n1. Launch Intiface,<br>\n2. Intiface: Start a server, <br>\n3. This page: click on [Connect controller]<br>\n4. Your hands: turn on your controller,<br>\n5. Intiface: Devices: Scan for devices, <br>\n6. This page: select your controller on this page, <br>\n7. This page: click on [Test your controller] =&gt; Your controller should react!<br>\n8. This page: Browse select the 2 files (they must have the same name), <br>\n9. This page: Click [Play], <br>\n10. Enjoy &lt;3\n<br><br>\n\ud83c\udd95 First time? Read the complete setup guide here \u2192 <a href=\"https:\/\/www.patreon.com\/posts\/85042659\" target=\"_blank\" rel=\"noopener\" style=\"text-decoration: underline\">link to detailed info<\/a>.<\/p><\/h6>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-165d8e3 elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"165d8e3\" data-element_type=\"section\" data-e-type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-e8b6ed9\" data-id=\"e8b6ed9\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-834ba55 elementor-widget elementor-widget-html\" data-id=\"834ba55\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>AudioSync - Immersive Audio Experience<\/title>\n    <style>\n        #media-player {\n            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n            max-width: 600px;\n            margin: 0 auto;\n            padding: 25px;\n            border: 1px solid #e0e0e0;\n            border-radius: 15px;\n            box-shadow: 0 5px 20px rgba(0, 0, 0, 0.08);\n            background: linear-gradient(135deg, #f8f9ff 0%, #f5f7ff 100%);\n        }\n        #media-player h2 {\n            text-align: center;\n            margin-bottom: 25px;\n            color: #4a4a9c;\n            font-weight: 600;\n            font-size: 24px;\n        }\n        #media-player .tagline {\n            text-align: center;\n            color: #666;\n            margin-bottom: 25px;\n            font-style: italic;\n            font-size: 14px;\n        }\n        #media-player input[type=\"file\"], #media-player button, #media-player select {\n            display: block;\n            width: 100%;\n            margin-bottom: 12px;\n            padding: 12px 15px;\n            font-size: 15px;\n            border: 2px solid #e0e0e0;\n            border-radius: 8px;\n            transition: all 0.3s ease;\n            box-sizing: border-box;\n        }\n        #media-player input[type=\"file\"]:focus, #media-player select:focus {\n            outline: none;\n            border-color: #4a4a9c;\n            box-shadow: 0 0 0 3px rgba(74, 74, 156, 0.1);\n        }\n        #media-player button {\n            background: linear-gradient(135deg, #4a4a9c 0%, #6a6ac9 100%);\n            color: white;\n            border: none;\n            cursor: pointer;\n            font-weight: 500;\n            letter-spacing: 0.3px;\n        }\n        #media-player button:hover:not(:disabled) {\n            transform: translateY(-1px);\n            box-shadow: 0 4px 12px rgba(74, 74, 156, 0.2);\n        }\n        #media-player button:active:not(:disabled) {\n            transform: translateY(0);\n        }\n        #media-player button:disabled {\n            background: #cccccc;\n            cursor: not-allowed;\n            transform: none;\n            box-shadow: none;\n        }\n        #media-player #connect-intiface {\n            background: linear-gradient(135deg, #007bff 0%, #0056b3 100%);\n        }\n        #media-player #stop-button {\n            background: linear-gradient(135deg, #dc3545 0%, #a71d2a 100%);\n        }\n        #media-player #test-button {\n            background: linear-gradient(135deg, #ffc107 0%, #e0a800 100%);\n            color: #212529;\n        }\n        #media-player #pause-button {\n            background: linear-gradient(135deg, #17a2b8 0%, #138496 100%);\n        }\n        #media-player audio {\n            width: 100%;\n            margin: 20px 0;\n            border-radius: 8px;\n        }\n        #status {\n            margin-top: 20px;\n            padding: 12px;\n            border-radius: 8px;\n            text-align: center;\n            font-weight: 500;\n            font-size: 14px;\n        }\n        .connected {\n            background-color: rgba(212, 237, 218, 0.8);\n            color: #155724;\n            border: 2px solid #c3e6cb;\n        }\n        .disconnected {\n            background-color: rgba(248, 215, 218, 0.8);\n            color: #721c24;\n            border: 2px solid #f5c6cb;\n        }\n        .scanning {\n            background-color: rgba(255, 243, 205, 0.8);\n            color: #856404;\n            border: 2px solid #ffeaa7;\n        }\n        #connection-settings {\n            margin-top: 20px;\n            padding: 18px;\n            background-color: rgba(233, 236, 239, 0.7);\n            border-radius: 10px;\n            transition: all 0.3s ease;\n            backdrop-filter: blur(5px);\n        }\n        #connection-settings.connected {\n            background-color: rgba(212, 237, 218, 0.8);\n            border: 2px solid #c3e6cb;\n        }\n        #connection-settings.scanning {\n            background-color: rgba(255, 243, 205, 0.8);\n            border: 2px solid #ffeaa7;\n        }\n        #connection-settings label {\n            display: block;\n            margin-bottom: 6px;\n            font-weight: 600;\n            color: #555;\n            font-size: 14px;\n        }\n        #connection-settings input, #connection-settings select {\n            width: 100%;\n            padding: 10px;\n            margin-bottom: 12px;\n            border: 2px solid #ddd;\n            border-radius: 6px;\n            font-size: 14px;\n            background: white;\n        }\n        #device-info {\n            margin-top: 20px;\n            padding: 15px;\n            background-color: rgba(232, 244, 248, 0.8);\n            border-radius: 10px;\n            border: 2px solid #b6e0fe;\n            backdrop-filter: blur(5px);\n        }\n        #device-name {\n            font-weight: 600;\n            margin-bottom: 5px;\n            color: #2c5282;\n        }\n        .device-type-badge {\n            display: inline-block;\n            padding: 4px 10px;\n            border-radius: 15px;\n            font-size: 11px;\n            font-weight: 700;\n            margin-left: 10px;\n            text-transform: uppercase;\n            letter-spacing: 0.5px;\n        }\n        .device-linear {\n            background: linear-gradient(135deg, #4CAF50 0%, #388E3C 100%);\n            color: white;\n        }\n        .device-vibrate {\n            background: linear-gradient(135deg, #9C27B0 0%, #7B1FA2 100%);\n            color: white;\n        }\n        .device-rotate {\n            background: linear-gradient(135deg, #FF9800 0%, #F57C00 100%);\n            color: white;\n        }\n        .device-oscillate {\n            background: linear-gradient(135deg, #2196F3 0%, #1976D2 100%);\n            color: white;\n        }\n        .hidden {\n            display: none;\n        }\n        #progress-container {\n            margin-top: 20px;\n            position: relative;\n        }\n        #progress-bar {\n            height: 8px;\n            background: #e0e0e0;\n            border-radius: 4px;\n            overflow: hidden;\n            cursor: pointer;\n            position: relative;\n        }\n        #progress-fill {\n            height: 100%;\n            background: linear-gradient(90deg, #4a4a9c 0%, #6a6ac9 100%);\n            width: 0%;\n            transition: width 0.1s ease;\n            position: relative;\n        }\n        #progress-bar:hover #progress-fill {\n            background: linear-gradient(90deg, #5a5aac 0%, #7a7ad9 100%);\n        }\n        #progress-bar:active #progress-fill {\n            background: linear-gradient(90deg, #3a3a8c 0%, #5a5ab9 100%);\n        }\n        #time-display {\n            display: flex;\n            justify-content: space-between;\n            margin-top: 8px;\n            font-size: 13px;\n            color: #666;\n        }\n        .debug-info {\n            margin-top: 15px;\n            padding: 12px;\n            background-color: rgba(248, 249, 250, 0.8);\n            border: 1px solid #dee2e6;\n            border-radius: 8px;\n            font-size: 11px;\n            color: #666;\n            max-height: 120px;\n            overflow-y: auto;\n            font-family: 'Courier New', monospace;\n            backdrop-filter: blur(5px);\n        }\n        .device-list {\n            margin-top: 10px;\n            padding: 12px;\n            background-color: rgba(240, 248, 255, 0.8);\n            border: 2px solid #cce5ff;\n            border-radius: 8px;\n            backdrop-filter: blur(5px);\n        }\n        .device-item {\n            padding: 10px;\n            margin: 8px 0;\n            background-color: rgba(255, 255, 255, 0.9);\n            border: 2px solid #ddd;\n            border-radius: 6px;\n            cursor: pointer;\n            transition: all 0.2s ease;\n        }\n        .device-item:hover {\n            background-color: rgba(240, 240, 255, 0.9);\n            border-color: #a0a0e0;\n            transform: translateX(2px);\n        }\n        .device-item.selected {\n            background-color: rgba(212, 237, 218, 0.9);\n            border-color: #c3e6cb;\n        }\n        .file-input-label {\n            display: block;\n            margin-bottom: 5px;\n            font-weight: 600;\n            color: #555;\n            font-size: 14px;\n        }\n        .control-buttons {\n            display: grid;\n            grid-template-columns: 1fr 1fr 1fr;\n            gap: 10px;\n            margin-top: 10px;\n            position: relative;\n        }\n        .control-buttons button {\n            margin-bottom: 0;\n        }\n        .file-inputs {\n            display: grid;\n            grid-template-columns: 1fr 1fr;\n            gap: 10px;\n            margin-bottom: 15px;\n        }\n        .file-inputs input[type=\"file\"] {\n            margin-bottom: 0;\n        }\n        audio::-webkit-media-controls-panel {\n            background-color: #f8f9ff;\n            border-radius: 8px;\n        }\n        audio::-webkit-media-controls-current-time-display,\n        audio::-webkit-media-controls-time-remaining-display {\n            font-family: 'Segoe UI', sans-serif;\n        }\n        \n        \/* Volume Control Styles *\/\n        .volume-container {\n            position: absolute;\n            right: -40px;\n            top: 0;\n            width: 30px;\n            height: 140px;\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            justify-content: center;\n            background: rgba(255, 255, 255, 0.9);\n            border-radius: 15px;\n            padding: 10px 0;\n            box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1);\n            border: 1px solid #e0e0e0;\n        }\n        \n        .volume-slider-container {\n            height: 100px;\n            width: 100%;\n            display: flex;\n            justify-content: center;\n            align-items: center;\n            position: relative;\n        }\n        \n        .volume-slider {\n            -webkit-appearance: none;\n            appearance: none;\n            width: 100px;\n            height: 6px;\n            transform: rotate(-90deg);\n            background: linear-gradient(to right, #4a4a9c 0%, #e0e0e0 0%);\n            border-radius: 3px;\n            outline: none;\n            position: absolute;\n            cursor: pointer;\n        }\n        \n        .volume-slider::-webkit-slider-thumb {\n            -webkit-appearance: none;\n            appearance: none;\n            width: 18px;\n            height: 18px;\n            border-radius: 50%;\n            background: #4a4a9c;\n            cursor: pointer;\n            border: 2px solid white;\n            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);\n            transition: all 0.2s ease;\n        }\n        \n        .volume-slider::-webkit-slider-thumb:hover {\n            background: #6a6ac9;\n            transform: scale(1.1);\n        }\n        \n        .volume-slider::-moz-range-thumb {\n            width: 18px;\n            height: 18px;\n            border-radius: 50%;\n            background: #4a4a9c;\n            cursor: pointer;\n            border: 2px solid white;\n            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);\n        }\n        \n        .volume-slider::-moz-range-thumb:hover {\n            background: #6a6ac9;\n        }\n        \n        .volume-icon {\n            margin-top: 8px;\n            font-size: 16px;\n            color: #4a4a9c;\n            cursor: pointer;\n            transition: all 0.2s ease;\n            padding: 5px;\n        }\n        \n        .volume-icon:hover {\n            color: #6a6ac9;\n            transform: scale(1.1);\n        }\n        \n        .volume-display {\n            font-size: 10px;\n            color: #666;\n            margin-top: 5px;\n            font-weight: 600;\n        }\n        \n        .muted {\n            color: #dc3545 !important;\n        }\n        \n        \/* Responsive adjustments *\/\n        @media (max-width: 680px) {\n            .volume-container {\n                position: static;\n                width: 100%;\n                height: auto;\n                margin-top: 10px;\n                flex-direction: row;\n                padding: 10px;\n                justify-content: space-between;\n            }\n            \n            .volume-slider-container {\n                height: auto;\n                width: 70%;\n            }\n            \n            .volume-slider {\n                transform: rotate(0deg);\n                width: 100%;\n                position: static;\n            }\n            \n            .volume-icon {\n                margin-top: 0;\n            }\n            \n            .control-buttons {\n                grid-template-columns: 1fr 1fr;\n            }\n        }\n    <\/style>\n<\/head>\n<body>\n    <div id=\"media-player\">\n        <h2>AudioSync Experience<\/h2>\n        <div class=\"tagline\">Immerse yourself in synchronized audio with interactive feedback<\/div>\n        \n        <div id=\"connection-settings\">\n            <label for=\"intiface-url\">Connection Server:<\/label>\n            <input type=\"text\" id=\"intiface-url\" value=\"ws:\/\/localhost:12345\" placeholder=\"ws:\/\/localhost:12345\">\n            <button id=\"connect-intiface\">Connect Controller<\/button>\n            <div id=\"connection-status\" class=\"disconnected\">Ready to connect<\/div>\n            \n            <div id=\"device-selection\" class=\"hidden\">\n                <label for=\"device-select\">Select Your Controller:<\/label>\n                <select id=\"device-select\">\n                    <option value=\"\">-- Choose a controller --<\/option>\n                <\/select>\n                <div id=\"device-list\" class=\"device-list\"><\/div>\n            <\/div>\n        <\/div>\n        \n        <div id=\"device-info\" class=\"hidden\">\n            <h3 style=\"margin-top: 0; color: #4a4a9c;\">Connected Controller:<\/h3>\n            <div id=\"device-display\">\n                <span id=\"device-name\">No controller connected<\/span>\n                <span id=\"device-type\" class=\"device-type-badge\"><\/span>\n            <\/div>\n            <div id=\"device-features\" style=\"font-size: 12px; color: #666; margin-top: 5px;\"><\/div>\n            <button id=\"test-button\" style=\"margin-top: 10px;\">Test Controller<\/button>\n        <\/div>\n        \n        <div class=\"file-inputs\">\n            <div>\n                <label class=\"file-input-label\">Timing File:<\/label>\n                <input type=\"file\" id=\"funscript-file\" accept=\".funscript\">\n            <\/div>\n            <div>\n                <label class=\"file-input-label\">Audio File:<\/label>\n                <input type=\"file\" id=\"mp3-file\" accept=\".mp3,.wav,.ogg,.m4a\">\n            <\/div>\n        <\/div>\n        \n        <div style=\"position: relative;\">\n            <div class=\"control-buttons\">\n                <button id=\"play-button\" disabled>\u25b6 Play<\/button>\n                <button id=\"pause-button\" disabled>\u23f8 Pause<\/button>\n                <button id=\"stop-button\" disabled>\u23f9 Stop<\/button>\n                \n                <!-- Volume Control -->\n                <div class=\"volume-container\">\n                    <div class=\"volume-slider-container\">\n                        <input type=\"range\" min=\"0\" max=\"100\" value=\"80\" class=\"volume-slider\" id=\"volume-slider\">\n                    <\/div>\n                    <div class=\"volume-icon\" id=\"volume-icon\">\ud83d\udd0a<\/div>\n                    <div class=\"volume-display\" id=\"volume-display\">80%<\/div>\n                <\/div>\n            <\/div>\n        <\/div>\n        \n        <div id=\"progress-container\">\n            <div id=\"progress-bar\">\n                <div id=\"progress-fill\"><\/div>\n            <\/div>\n            <div id=\"time-display\">\n                <span id=\"current-time\">0:00<\/span>\n                <span id=\"total-time\">0:00<\/span>\n            <\/div>\n        <\/div>\n        \n        <audio id=\"audio-player\"><\/audio>\n        \n        <div id=\"status\" class=\"disconnected\">Load files to begin<\/div>\n        \n        <div class=\"debug-info\">\n            <div id=\"debug-log\">System ready...<\/div>\n        <\/div>\n        \n        <div style=\"margin-top: 10px; padding: 10px; background: #f8f9fa; border-radius: 5px; font-size: 12px;\">\n            <div><strong>Connection Status:<\/strong> <span id=\"connection-details\">Not connected<\/span><\/div>\n            <div><strong>Buttplug Version:<\/strong> <span id=\"buttplug-version\">Unknown<\/span><\/div>\n        <\/div>\n    <\/div>\n\n    <script>\n    document.addEventListener('DOMContentLoaded', function() {\n        \/\/ Device type definitions\n        const DEVICE_TYPES = {\n            LINEAR: 'linear',\n            VIBRATE: 'vibrate',\n            ROTATE: 'rotate',\n            OSCILLATE: 'oscillate'\n        };\n\n        \/\/ DOM Elements\n        const elements = {\n            funscriptFileInput: document.getElementById('funscript-file'),\n            mp3FileInput: document.getElementById('mp3-file'),\n            playButton: document.getElementById('play-button'),\n            stopButton: document.getElementById('stop-button'),\n            pauseButton: document.getElementById('pause-button'),\n            testButton: document.getElementById('test-button'),\n            connectIntifaceButton: document.getElementById('connect-intiface'),\n            audioPlayer: document.getElementById('audio-player'),\n            intifaceUrlInput: document.getElementById('intiface-url'),\n            connectionStatus: document.getElementById('connection-status'),\n            connectionSettings: document.getElementById('connection-settings'),\n            statusDiv: document.getElementById('status'),\n            deviceInfo: document.getElementById('device-info'),\n            deviceName: document.getElementById('device-name'),\n            deviceType: document.getElementById('device-type'),\n            deviceFeatures: document.getElementById('device-features'),\n            deviceSelect: document.getElementById('device-select'),\n            deviceSelection: document.getElementById('device-selection'),\n            deviceList: document.getElementById('device-list'),\n            progressBar: document.getElementById('progress-bar'),\n            progressFill: document.getElementById('progress-fill'),\n            currentTimeDisplay: document.getElementById('current-time'),\n            totalTimeDisplay: document.getElementById('total-time'),\n            debugLog: document.getElementById('debug-log'),\n            volumeSlider: document.getElementById('volume-slider'),\n            volumeIcon: document.getElementById('volume-icon'),\n            volumeDisplay: document.getElementById('volume-display'),\n            connectionDetails: document.getElementById('connection-details'),\n            buttplugVersion: document.getElementById('buttplug-version')\n        };\n\n        \/\/ State variables\n        let state = {\n            funscriptData: null,\n            mp3File: null,\n            buttplugClient: null,\n            currentDevice: null,\n            availableDevices: [],\n            deviceType: null,\n            isPlaying: false,\n            isPaused: false,\n            scriptInterval: null,\n            scriptStartTime: 0,\n            audioStartTime: 0,\n            totalScriptTime: 0,\n            pauseOffset: 0,\n            isSeeking: false,\n            volume: 0.8,\n            isMuted: false,\n            lastVolume: 0.8\n        };\n\n        function logDebug(message) {\n            const timestamp = new Date().toLocaleTimeString();\n            elements.debugLog.innerHTML = `<span style=\"color:#666\">${timestamp}:<\/span> ${message}<br>${elements.debugLog.innerHTML}`;\n            console.log(message);\n        }\n\n        function loadButtplugLibrary() {\n            return new Promise((resolve, reject) => {\n                if (window.Buttplug) {\n                    logDebug('Buttplug already loaded');\n                    resolve();\n                    return;\n                }\n                \n                \/\/ Pre-load Buttplug library directly in the head\n                const script = document.createElement('script');\n                script.src = 'https:\/\/cdn.jsdelivr.net\/npm\/buttplug@3.0.0\/dist\/web\/buttplug.min.js';\n                \n                script.onload = function() {\n                    logDebug('Buttplug library loaded');\n                    if (window.Buttplug) {\n                        elements.buttplugVersion.textContent = '3.0.0';\n                        resolve();\n                    } else {\n                        reject(new Error('Buttplug object not found after loading'));\n                    }\n                };\n                \n                script.onerror = function() {\n                    reject(new Error('Failed to load Buttplug library from CDN'));\n                };\n                \n                \/\/ Add to head instead of body\n                document.head.appendChild(script);\n                \n                \/\/ Set timeout to prevent hanging\n                setTimeout(() => {\n                    if (!window.Buttplug) {\n                        reject(new Error('Timeout loading Buttplug library'));\n                    }\n                }, 5000);\n            });\n        }\n\n        \/\/ Load Buttplug library immediately when page loads\n        loadButtplugLibrary().then(() => {\n            logDebug('Buttplug library pre-loaded successfully');\n            elements.connectionDetails.textContent = 'Library loaded';\n        }).catch(error => {\n            logDebug(`Failed to pre-load library: ${error.message}`);\n            elements.connectionDetails.textContent = 'Library failed to load';\n        });\n\n        \/\/ Volume Control Functions\n        function updateVolumeSlider() {\n            const volumePercent = state.volume * 100;\n            elements.volumeSlider.value = volumePercent;\n            elements.volumeDisplay.textContent = `${Math.round(volumePercent)}%`;\n            \n            elements.volumeSlider.style.background = \n                `linear-gradient(to right, #4a4a9c 0%, #4a4a9c ${volumePercent}%, #e0e0e0 ${volumePercent}%, #e0e0e0 100%)`;\n            \n            if (state.volume === 0 || state.isMuted) {\n                elements.volumeIcon.textContent = '\ud83d\udd07';\n                elements.volumeIcon.classList.add('muted');\n            } else if (state.volume < 0.3) {\n                elements.volumeIcon.textContent = '\ud83d\udd08';\n                elements.volumeIcon.classList.remove('muted');\n            } else if (state.volume < 0.7) {\n                elements.volumeIcon.textContent = '\ud83d\udd09';\n                elements.volumeIcon.classList.remove('muted');\n            } else {\n                elements.volumeIcon.textContent = '\ud83d\udd0a';\n                elements.volumeIcon.classList.remove('muted');\n            }\n            \n            elements.audioPlayer.volume = state.isMuted ? 0 : state.volume;\n        }\n\n        function toggleMute() {\n            if (state.isMuted) {\n                state.isMuted = false;\n                state.volume = state.lastVolume;\n            } else {\n                state.lastVolume = state.volume;\n                state.volume = 0;\n                state.isMuted = true;\n            }\n            updateVolumeSlider();\n            logDebug(`Volume ${state.isMuted ? 'muted' : 'unmuted'} (${Math.round(state.volume * 100)}%)`);\n        }\n\n        function setVolume(value) {\n            const newVolume = Math.max(0, Math.min(1, value));\n            state.volume = newVolume;\n            state.isMuted = false;\n            updateVolumeSlider();\n            logDebug(`Volume set to ${Math.round(state.volume * 100)}%`);\n        }\n\n        \/\/ Initialize volume\n        updateVolumeSlider();\n\n        \/\/ Volume event listeners\n        elements.volumeSlider.addEventListener('input', function() {\n            setVolume(this.value \/ 100);\n        });\n\n        elements.volumeIcon.addEventListener('click', toggleMute);\n\n        \/\/ Volume keyboard shortcuts\n        document.addEventListener('keydown', function(e) {\n            if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;\n            \n            switch(e.key) {\n                case 'ArrowUp':\n                    e.preventDefault();\n                    setVolume(Math.min(1, state.volume + 0.1));\n                    break;\n                case 'ArrowDown':\n                    e.preventDefault();\n                    setVolume(Math.max(0, state.volume - 0.1));\n                    break;\n                case 'm':\n                case 'M':\n                    e.preventDefault();\n                    toggleMute();\n                    break;\n            }\n        });\n\n        function detectDeviceType(device) {\n            const features = device.AllowedMessages || [];\n            const deviceName = device.name.toLowerCase();\n            \n            if (features.includes('LinearCmd') || deviceName.includes('handy') || \n                deviceName.includes('launch') || deviceName.includes('stroke') ||\n                deviceName.includes('osr')) {\n                return DEVICE_TYPES.LINEAR;\n            } else if (features.includes('VibrateCmd') || deviceName.includes('lovense') || \n                       deviceName.includes('max') || deviceName.includes('nora') ||\n                       deviceName.includes('domi') || deviceName.includes('vibr')) {\n                return DEVICE_TYPES.VIBRATE;\n            } else if (features.includes('RotateCmd')) {\n                return DEVICE_TYPES.ROTATE;\n            } else if (features.includes('OscillateCmd')) {\n                return DEVICE_TYPES.OSCILLATE;\n            }\n            \n            return DEVICE_TYPES.VIBRATE;\n        }\n\n        function updateDeviceDisplay(device) {\n            if (!device) {\n                elements.deviceInfo.classList.add('hidden');\n                elements.deviceName.textContent = 'No controller connected';\n                elements.deviceType.textContent = '';\n                elements.deviceType.className = 'device-type-badge';\n                elements.deviceFeatures.innerHTML = '';\n                return;\n            }\n            \n            state.deviceType = detectDeviceType(device);\n            const typeName = state.deviceType.charAt(0).toUpperCase() + state.deviceType.slice(1);\n            \n            elements.deviceInfo.classList.remove('hidden');\n            elements.deviceName.textContent = device.name;\n            elements.deviceType.textContent = typeName;\n            elements.deviceType.className = `device-type-badge device-${state.deviceType}`;\n            \n            const features = device.AllowedMessages || [];\n            elements.deviceFeatures.innerHTML = `Type: ${typeName} device`;\n            \n            elements.testButton.classList.remove('hidden');\n            \n            const select = elements.deviceSelect;\n            select.innerHTML = '<option value=\"\">-- Choose a controller --<\/option>';\n            state.availableDevices.forEach((dev, index) => {\n                const option = document.createElement('option');\n                option.value = index;\n                option.textContent = `${dev.name}`;\n                if (dev === device) {\n                    option.selected = true;\n                }\n                select.appendChild(option);\n            });\n            \n            logDebug(`Controller connected: ${device.name} (${state.deviceType})`);\n        }\n\n        async function sendDeviceCommand(position) {\n            if (!state.currentDevice) return;\n            \n            try {\n                const device = state.currentDevice;\n                \n                switch (state.deviceType) {\n                    case DEVICE_TYPES.LINEAR:\n                        if (device.linear) {\n                            await device.linear(position, 80);\n                        }\n                        break;\n                    case DEVICE_TYPES.VIBRATE:\n                        if (device.vibrate) {\n                            await device.vibrate(position);\n                        }\n                        break;\n                    case DEVICE_TYPES.ROTATE:\n                        if (device.rotate) {\n                            await device.rotate(position, true);\n                        }\n                        break;\n                    case DEVICE_TYPES.OSCILLATE:\n                        if (device.oscillate) {\n                            await device.oscillate(position);\n                        }\n                        break;\n                    default:\n                        if (device.vibrate) await device.vibrate(position);\n                        else if (device.linear) await device.linear(position, 80);\n                        break;\n                }\n            } catch (error) {\n                logDebug(`Command error: ${error.message}`);\n            }\n        }\n\n        async function testDevice() {\n            if (!state.currentDevice) return;\n            \n            try {\n                logDebug(`Testing ${state.deviceType} controller...`);\n                const device = state.currentDevice;\n                \n                switch (state.deviceType) {\n                    case DEVICE_TYPES.LINEAR:\n                        if (device.linear) {\n                            await device.linear(0.3, 400);\n                            setTimeout(async () => {\n                                await device.linear(0.7, 400);\n                                setTimeout(async () => {\n                                    await device.linear(0, 400);\n                                }, 400);\n                            }, 400);\n                        }\n                        break;\n                    case DEVICE_TYPES.VIBRATE:\n                        if (device.vibrate) {\n                            await device.vibrate(0.3);\n                            setTimeout(async () => {\n                                await device.vibrate(0.6);\n                                setTimeout(async () => {\n                                    await device.vibrate(0);\n                                }, 400);\n                            }, 400);\n                        }\n                        break;\n                }\n                logDebug('Controller test complete');\n            } catch (error) {\n                logDebug(`Test error: ${error.message}`);\n            }\n        }\n\n        async function testWebSocketConnection(url) {\n            return new Promise((resolve) => {\n                logDebug(`Testing WebSocket connection to: ${url}`);\n                \n                const ws = new WebSocket(url);\n                let connected = false;\n                \n                ws.onopen = function() {\n                    connected = true;\n                    logDebug('WebSocket test: Connection successful');\n                    ws.close();\n                    resolve(true);\n                };\n                \n                ws.onerror = function(error) {\n                    logDebug(`WebSocket test: Connection failed`);\n                    resolve(false);\n                };\n                \n                ws.onclose = function() {\n                    if (!connected) {\n                        logDebug('WebSocket test: Connection closed without opening');\n                        resolve(false);\n                    }\n                };\n                \n                setTimeout(() => {\n                    if (ws.readyState !== WebSocket.OPEN) {\n                        logDebug('WebSocket test: Timeout');\n                        ws.close();\n                        resolve(false);\n                    }\n                }, 3000);\n            });\n        }\n\n        async function connectToIntiface() {\n            try {\n                elements.statusDiv.textContent = 'Connecting...';\n                elements.statusDiv.className = 'scanning';\n                logDebug('Starting connection...');\n                \n                elements.connectionDetails.textContent = 'Loading library...';\n                \n                \/\/ Check if Buttplug is loaded\n                if (!window.Buttplug) {\n                    logDebug('Buttplug not loaded, attempting to load...');\n                    await loadButtplugLibrary();\n                }\n                \n                if (!window.Buttplug) {\n                    throw new Error('Buttplug library failed to load');\n                }\n                \n                logDebug('Buttplug library is loaded');\n                elements.connectionDetails.textContent = 'Library loaded';\n                \n                const serverUrl = elements.intifaceUrlInput.value;\n                logDebug(`Connecting to server: ${serverUrl}`);\n                \n                \/\/ Test WebSocket connection\n                elements.connectionDetails.textContent = 'Testing connection...';\n                const canConnect = await testWebSocketConnection(serverUrl);\n                \n                if (!canConnect) {\n                    throw new Error('Cannot connect to Intiface Central. Make sure it is running on ' + serverUrl);\n                }\n                \n                logDebug('Creating Buttplug client...');\n                elements.connectionDetails.textContent = 'Creating client...';\n                state.buttplugClient = new Buttplug.ButtplugClient(\"AudioSync Player\");\n                \n                logDebug('Creating connector...');\n                const connector = new Buttplug.ButtplugBrowserWebsocketClientConnector(serverUrl);\n                \n                logDebug('Connecting to server...');\n                elements.connectionDetails.textContent = 'Connecting to server...';\n                await state.buttplugClient.connect(connector);\n                \n                logDebug('Connected! Starting scan...');\n                elements.connectionDetails.textContent = 'Connected, scanning...';\n                await state.buttplugClient.startScanning();\n                \n                elements.connectionStatus.textContent = 'Connected - Scanning for controllers...';\n                elements.connectionStatus.className = 'scanning';\n                elements.connectionSettings.className = 'scanning';\n                elements.statusDiv.textContent = 'Connected! Looking for controllers...';\n                elements.statusDiv.className = 'scanning';\n                \n                elements.deviceSelection.classList.remove('hidden');\n                \n                \/\/ Device event listeners\n                state.buttplugClient.addListener(\"deviceadded\", (device) => {\n                    logDebug(`Controller detected: ${device.name}`);\n                    \n                    if (!state.availableDevices.some(d => d.index === device.index)) {\n                        state.availableDevices.push(device);\n                        updateDeviceList();\n                    }\n                    \n                    if (!state.currentDevice && state.availableDevices.length === 1) {\n                        selectDevice(device);\n                    }\n                });\n                \n                state.buttplugClient.addListener(\"deviceremoved\", (device) => {\n                    logDebug(`Controller removed: ${device.name}`);\n                    \n                    state.availableDevices = state.availableDevices.filter(d => d.index !== device.index);\n                    updateDeviceList();\n                    \n                    if (state.currentDevice && state.currentDevice.index === device.index) {\n                        state.currentDevice = null;\n                        updateDeviceDisplay(null);\n                        updatePlayButtonState();\n                        elements.statusDiv.textContent = 'Controller disconnected';\n                        elements.statusDiv.className = 'disconnected';\n                    }\n                });\n                \n                \/\/ Auto-stop scanning after 15 seconds\n                setTimeout(async () => {\n                    try {\n                        if (state.buttplugClient) {\n                            await state.buttplugClient.stopScanning();\n                            logDebug('Auto-stopped scanning');\n                            if (!state.currentDevice) {\n                                elements.connectionStatus.textContent = 'Scan complete. Select a controller.';\n                                elements.connectionStatus.className = 'disconnected';\n                                elements.connectionSettings.className = '';\n                            }\n                        }\n                    } catch (error) {\n                        logDebug(`Stop scan error: ${error.message}`);\n                    }\n                }, 15000);\n                \n                logDebug('Connection successful!');\n                elements.connectionDetails.textContent = 'Connected to server';\n                \n            } catch (error) {\n                const errorMessage = error.message || 'Unknown connection error';\n                \n                logDebug(`Connection error: ${errorMessage}`);\n                console.error('Connection error details:', error);\n                \n                elements.connectionStatus.textContent = `Connection failed: ${errorMessage}`;\n                elements.connectionStatus.className = 'disconnected';\n                elements.connectionSettings.className = '';\n                elements.statusDiv.textContent = `Connection error: ${errorMessage}`;\n                elements.statusDiv.className = 'disconnected';\n                elements.connectionDetails.textContent = `Failed: ${errorMessage}`;\n                \n                state.buttplugClient = null;\n            }\n        }\n\n        function updateDeviceList() {\n            const list = elements.deviceList;\n            list.innerHTML = '';\n            \n            state.availableDevices.forEach((device) => {\n                const deviceType = detectDeviceType(device);\n                const typeName = deviceType.charAt(0).toUpperCase() + deviceType.slice(1);\n                \n                const item = document.createElement('div');\n                item.className = `device-item ${state.currentDevice === device ? 'selected' : ''}`;\n                item.innerHTML = `\n                    <strong>${device.name}<\/strong>\n                    <span class=\"device-type-badge device-${deviceType}\">${typeName}<\/span>\n                `;\n                item.addEventListener('click', () => selectDevice(device));\n                list.appendChild(item);\n            });\n            \n            const select = elements.deviceSelect;\n            select.innerHTML = '<option value=\"\">-- Choose a controller --<\/option>';\n            state.availableDevices.forEach((device, index) => {\n                const option = document.createElement('option');\n                option.value = index;\n                option.textContent = device.name;\n                if (device === state.currentDevice) {\n                    option.selected = true;\n                }\n                select.appendChild(option);\n            });\n        }\n\n        function selectDevice(device) {\n            state.currentDevice = device;\n            updateDeviceDisplay(device);\n            updatePlayButtonState();\n            \n            elements.statusDiv.textContent = `Controller connected: ${device.name}`;\n            elements.statusDiv.className = 'connected';\n            \n            logDebug(`Selected controller: ${device.name}`);\n        }\n\n        \/\/ Event Listeners\n        elements.deviceSelect.addEventListener('change', function() {\n            const index = parseInt(this.value);\n            if (!isNaN(index) && state.availableDevices[index]) {\n                selectDevice(state.availableDevices[index]);\n            }\n        });\n\n        elements.testButton.addEventListener('click', testDevice);\n        elements.connectIntifaceButton.addEventListener('click', connectToIntiface);\n\n        \/\/ Click on progress bar to seek\n        elements.progressBar.addEventListener('click', function(e) {\n            if (!state.funscriptData || !state.mp3File) return;\n            \n            const rect = this.getBoundingClientRect();\n            const clickX = e.clientX - rect.left;\n            const percent = clickX \/ rect.width;\n            \n            const seekTime = percent * state.totalScriptTime;\n            \n            if (state.isPlaying) {\n                state.scriptStartTime = Date.now() - seekTime;\n                elements.audioPlayer.currentTime = seekTime \/ 1000;\n                elements.progressFill.style.width = (percent * 100) + '%';\n                elements.currentTimeDisplay.textContent = formatTime(seekTime);\n                \n                logDebug(`Seeked to ${formatTime(seekTime)}`);\n            }\n        });\n\n        elements.funscriptFileInput.addEventListener('change', function(event) {\n            const file = event.target.files[0];\n            if (!file) return;\n            \n            const reader = new FileReader();\n            reader.onload = function(e) {\n                try {\n                    state.funscriptData = JSON.parse(e.target.result);\n                    logDebug(`Timing file loaded: ${file.name} (${state.funscriptData.actions?.length || 0} events)`);\n                    \n                    if (state.funscriptData.actions?.length > 0) {\n                        const lastAction = state.funscriptData.actions[state.funscriptData.actions.length - 1];\n                        state.totalScriptTime = lastAction.at;\n                        elements.totalTimeDisplay.textContent = formatTime(state.totalScriptTime);\n                    }\n                    \n                    updatePlayButtonState();\n                } catch (error) {\n                    elements.statusDiv.textContent = 'Error loading timing file';\n                    elements.statusDiv.className = 'disconnected';\n                    logDebug(`File error: ${error.message}`);\n                }\n            };\n            reader.readAsText(file);\n        });\n\n        elements.mp3FileInput.addEventListener('change', function(event) {\n            state.mp3File = event.target.files[0];\n            if (state.mp3File) {\n                const url = URL.createObjectURL(state.mp3File);\n                elements.audioPlayer.src = url;\n                logDebug(`Audio loaded: ${state.mp3File.name}`);\n                \n                elements.audioPlayer.addEventListener('loadedmetadata', function() {\n                    elements.totalTimeDisplay.textContent = formatTime(elements.audioPlayer.duration * 1000);\n                });\n                \n                updatePlayButtonState();\n            }\n        });\n\n        function updatePlayButtonState() {\n            const readyToPlay = state.funscriptData && state.mp3File && state.currentDevice && !state.isPlaying;\n            elements.playButton.disabled = !readyToPlay;\n            elements.pauseButton.disabled = !state.isPlaying || state.isPaused;\n            elements.stopButton.disabled = !state.isPlaying;\n        }\n\n        function formatTime(ms) {\n            if (!ms || ms < 0) return '0:00';\n            const totalSeconds = Math.floor(ms \/ 1000);\n            const minutes = Math.floor(totalSeconds \/ 60);\n            const seconds = totalSeconds % 60;\n            return `${minutes}:${seconds.toString().padStart(2, '0')}`;\n        }\n\n        elements.playButton.addEventListener('click', async function() {\n            if (!state.funscriptData || !state.mp3File || !state.currentDevice || state.isPlaying) return;\n            \n            try {\n                state.isPlaying = true;\n                state.isPaused = false;\n                updatePlayButtonState();\n                \n                elements.playButton.textContent = '\u25b6 Playing';\n                elements.pauseButton.textContent = '\u23f8 Pause';\n                \n                if (state.isPaused) {\n                    elements.audioPlayer.currentTime = state.pauseOffset \/ 1000;\n                } else {\n                    state.pauseOffset = 0;\n                    elements.audioPlayer.currentTime = 0;\n                }\n                \n                await elements.audioPlayer.play();\n                state.audioStartTime = Date.now() - state.pauseOffset;\n                state.scriptStartTime = Date.now() - state.pauseOffset;\n                \n                \/\/ Process timing events\n                state.scriptInterval = setInterval(() => {\n                    const currentTime = Date.now() - state.scriptStartTime;\n                    \n                    \/\/ Update progress\n                    const progressPercent = (currentTime \/ state.totalScriptTime) * 100;\n                    elements.progressFill.style.width = Math.min(progressPercent, 100) + '%';\n                    elements.currentTimeDisplay.textContent = formatTime(currentTime);\n                    \n                    \/\/ Find and execute events\n                    const currentActions = state.funscriptData.actions.filter(action => \n                        action.at <= currentTime && action.at > currentTime - 50\n                    );\n                    \n                    if (currentActions.length > 0) {\n                        const action = currentActions[currentActions.length - 1];\n                        const position = action.pos \/ 100;\n                        sendDeviceCommand(position);\n                    }\n                    \n                    \/\/ Check if finished\n                    if (elements.audioPlayer.ended && !state.isPaused) {\n                        stopPlayback();\n                    }\n                }, 20);\n                \n                elements.statusDiv.textContent = 'Playing...';\n                logDebug('Playback started');\n            } catch (error) {\n                logDebug(`Playback error: ${error.message}`);\n                elements.statusDiv.textContent = `Playback error: ${error.message}`;\n                stopPlayback();\n            }\n        });\n\n        elements.pauseButton.addEventListener('click', function() {\n            if (!state.isPlaying) return;\n            \n            if (!state.isPaused) {\n                state.isPaused = true;\n                elements.audioPlayer.pause();\n                clearInterval(state.scriptInterval);\n                state.scriptInterval = null;\n                state.pauseOffset = Date.now() - state.scriptStartTime;\n                elements.pauseButton.textContent = '\u25b6 Resume';\n                elements.playButton.textContent = '\u25b6 Play';\n                elements.statusDiv.textContent = 'Paused';\n                logDebug('Playback paused');\n            } else {\n                state.isPaused = false;\n                elements.pauseButton.textContent = '\u23f8 Pause';\n                elements.playButton.click();\n            }\n        });\n\n        elements.stopButton.addEventListener('click', stopPlayback);\n\n        function stopPlayback() {\n            if (!state.isPlaying && !state.isPaused) return;\n            \n            state.isPlaying = false;\n            state.isPaused = false;\n            clearInterval(state.scriptInterval);\n            state.scriptInterval = null;\n            \n            elements.audioPlayer.pause();\n            elements.audioPlayer.currentTime = 0;\n            \n            elements.progressFill.style.width = '0%';\n            elements.currentTimeDisplay.textContent = '0:00';\n            elements.playButton.textContent = '\u25b6 Play';\n            elements.pauseButton.textContent = '\u23f8 Pause';\n            \n            if (state.currentDevice) {\n                try {\n                    if (state.currentDevice.stop) {\n                        state.currentDevice.stop();\n                    } else if (state.currentDevice.vibrate) {\n                        state.currentDevice.vibrate(0);\n                    } else if (state.currentDevice.linear) {\n                        state.currentDevice.linear(0, 100);\n                    }\n                } catch (error) {\n                    logDebug(`Stop error: ${error.message}`);\n                }\n            }\n            \n            updatePlayButtonState();\n            elements.statusDiv.textContent = state.currentDevice ? 'Ready to play' : 'Load files to begin';\n            \n            logDebug('Playback stopped');\n        }\n\n        \/\/ Audio player event listeners\n        elements.audioPlayer.addEventListener('play', function() {\n            if (!state.isPlaying && state.funscriptData) {\n                elements.playButton.click();\n            }\n        });\n\n        elements.audioPlayer.addEventListener('pause', function() {\n            if (state.isPlaying && !state.isPaused) {\n                elements.pauseButton.click();\n            }\n        });\n\n        elements.audioPlayer.addEventListener('ended', function() {\n            if (state.isPlaying && !state.isPaused) {\n                stopPlayback();\n            }\n        });\n\n        \/\/ Initialize\n        logDebug('AudioSync Experience ready');\n        elements.statusDiv.textContent = 'Load timing and audio files to begin';\n        \n        \/\/ Set initial volume on audio player\n        elements.audioPlayer.volume = state.volume;\n    });\n    <\/script>\n<\/body>\n<\/html>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Haptics Hi everyone! This works only with CHROME. We&#8217;re going to use a Bluetooth connection with your controller\/device. You need to use Intiface Central on your computer. You&#8217;ll need an mp3 file and a funscript file, provided in each post. See list of posts and more info on this page. 1. Launch Intiface, 2. Intiface: [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":4746,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"site-sidebar-layout":"no-sidebar","site-content-layout":"page-builder","ast-site-content-layout":"full-width-container","site-content-style":"unboxed","site-sidebar-style":"unboxed","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"disabled","ast-breadcrumbs-content":"","ast-featured-img":"disabled","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"set","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"class_list":["post-4734","page","type-page","status-publish","has-post-thumbnail","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.3 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Haptics - mastervincehypnosis.com<\/title>\n<meta name=\"description\" content=\"This page provides you a tool to synchronize audio sessions with their corresponding haptic script.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/mastervincehypnosis.com\/?page_id=4734\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Haptics - mastervincehypnosis.com\" \/>\n<meta property=\"og:description\" content=\"This page provides you a tool to synchronize audio sessions with their corresponding haptic script.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/mastervincehypnosis.com\/?page_id=4734\" \/>\n<meta property=\"og:site_name\" content=\"mastervincehypnosis.com\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/MasterVinceHypnosis\" \/>\n<meta property=\"article:modified_time\" content=\"2026-04-01T16:27:33+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/mastervincehypnosis.com\/wp-content\/uploads\/2025\/12\/Pat-Haptic-list-copie-V2.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"820\" \/>\n\t<meta property=\"og:image:height\" content=\"492\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:site\" content=\"@MVHypnosis\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"2 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/mastervincehypnosis.com\\\/?page_id=4734\",\"url\":\"https:\\\/\\\/mastervincehypnosis.com\\\/?page_id=4734\",\"name\":\"Haptics - mastervincehypnosis.com\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/mastervincehypnosis.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/mastervincehypnosis.com\\\/?page_id=4734#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/mastervincehypnosis.com\\\/?page_id=4734#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/mastervincehypnosis.com\\\/wp-content\\\/uploads\\\/2025\\\/12\\\/Pat-Haptic-list-copie-V2.jpg\",\"datePublished\":\"2025-12-06T14:01:14+00:00\",\"dateModified\":\"2026-04-01T16:27:33+00:00\",\"description\":\"This page provides you a tool to synchronize audio sessions with their corresponding haptic script.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/mastervincehypnosis.com\\\/?page_id=4734#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/mastervincehypnosis.com\\\/?page_id=4734\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/mastervincehypnosis.com\\\/?page_id=4734#primaryimage\",\"url\":\"https:\\\/\\\/mastervincehypnosis.com\\\/wp-content\\\/uploads\\\/2025\\\/12\\\/Pat-Haptic-list-copie-V2.jpg\",\"contentUrl\":\"https:\\\/\\\/mastervincehypnosis.com\\\/wp-content\\\/uploads\\\/2025\\\/12\\\/Pat-Haptic-list-copie-V2.jpg\",\"width\":820,\"height\":492},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/mastervincehypnosis.com\\\/?page_id=4734#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/mastervincehypnosis.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Haptics\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/mastervincehypnosis.com\\\/#website\",\"url\":\"https:\\\/\\\/mastervincehypnosis.com\\\/\",\"name\":\"mastervincehypnosis.com\",\"description\":\"Hypnotic audios for everybody\",\"publisher\":{\"@id\":\"https:\\\/\\\/mastervincehypnosis.com\\\/#\\\/schema\\\/person\\\/7a6c073117bef597a6afe22fb1081f3b\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/mastervincehypnosis.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/mastervincehypnosis.com\\\/#\\\/schema\\\/person\\\/7a6c073117bef597a6afe22fb1081f3b\",\"name\":\"mastervincehypnosis@hotmail.com\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/mastervincehypnosis.com\\\/wp-content\\\/uploads\\\/2022\\\/11\\\/Patreon-Cover-image-avatar-2.png\",\"url\":\"https:\\\/\\\/mastervincehypnosis.com\\\/wp-content\\\/uploads\\\/2022\\\/11\\\/Patreon-Cover-image-avatar-2.png\",\"contentUrl\":\"https:\\\/\\\/mastervincehypnosis.com\\\/wp-content\\\/uploads\\\/2022\\\/11\\\/Patreon-Cover-image-avatar-2.png\",\"width\":1024,\"height\":1024,\"caption\":\"mastervincehypnosis@hotmail.com\"},\"logo\":{\"@id\":\"https:\\\/\\\/mastervincehypnosis.com\\\/wp-content\\\/uploads\\\/2022\\\/11\\\/Patreon-Cover-image-avatar-2.png\"},\"sameAs\":[\"http:\\\/\\\/mastervincehypnosis.com\",\"https:\\\/\\\/www.facebook.com\\\/MasterVinceHypnosis\",\"https:\\\/\\\/www.instagram.com\\\/mastervincehypnosis\\\/\",\"https:\\\/\\\/www.pinterest.fr\\\/mastervincehypnosis\\\/\",\"https:\\\/\\\/x.com\\\/https:\\\/\\\/twitter.com\\\/MVHypnosis\",\"https:\\\/\\\/www.youtube.com\\\/@mastervincehypnosis\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Haptics - mastervincehypnosis.com","description":"This page provides you a tool to synchronize audio sessions with their corresponding haptic script.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/mastervincehypnosis.com\/?page_id=4734","og_locale":"en_US","og_type":"article","og_title":"Haptics - mastervincehypnosis.com","og_description":"This page provides you a tool to synchronize audio sessions with their corresponding haptic script.","og_url":"https:\/\/mastervincehypnosis.com\/?page_id=4734","og_site_name":"mastervincehypnosis.com","article_publisher":"https:\/\/www.facebook.com\/MasterVinceHypnosis","article_modified_time":"2026-04-01T16:27:33+00:00","og_image":[{"width":820,"height":492,"url":"https:\/\/mastervincehypnosis.com\/wp-content\/uploads\/2025\/12\/Pat-Haptic-list-copie-V2.jpg","type":"image\/jpeg"}],"twitter_card":"summary_large_image","twitter_site":"@MVHypnosis","twitter_misc":{"Est. reading time":"2 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/mastervincehypnosis.com\/?page_id=4734","url":"https:\/\/mastervincehypnosis.com\/?page_id=4734","name":"Haptics - mastervincehypnosis.com","isPartOf":{"@id":"https:\/\/mastervincehypnosis.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/mastervincehypnosis.com\/?page_id=4734#primaryimage"},"image":{"@id":"https:\/\/mastervincehypnosis.com\/?page_id=4734#primaryimage"},"thumbnailUrl":"https:\/\/mastervincehypnosis.com\/wp-content\/uploads\/2025\/12\/Pat-Haptic-list-copie-V2.jpg","datePublished":"2025-12-06T14:01:14+00:00","dateModified":"2026-04-01T16:27:33+00:00","description":"This page provides you a tool to synchronize audio sessions with their corresponding haptic script.","breadcrumb":{"@id":"https:\/\/mastervincehypnosis.com\/?page_id=4734#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/mastervincehypnosis.com\/?page_id=4734"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/mastervincehypnosis.com\/?page_id=4734#primaryimage","url":"https:\/\/mastervincehypnosis.com\/wp-content\/uploads\/2025\/12\/Pat-Haptic-list-copie-V2.jpg","contentUrl":"https:\/\/mastervincehypnosis.com\/wp-content\/uploads\/2025\/12\/Pat-Haptic-list-copie-V2.jpg","width":820,"height":492},{"@type":"BreadcrumbList","@id":"https:\/\/mastervincehypnosis.com\/?page_id=4734#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/mastervincehypnosis.com\/"},{"@type":"ListItem","position":2,"name":"Haptics"}]},{"@type":"WebSite","@id":"https:\/\/mastervincehypnosis.com\/#website","url":"https:\/\/mastervincehypnosis.com\/","name":"mastervincehypnosis.com","description":"Hypnotic audios for everybody","publisher":{"@id":"https:\/\/mastervincehypnosis.com\/#\/schema\/person\/7a6c073117bef597a6afe22fb1081f3b"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/mastervincehypnosis.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/mastervincehypnosis.com\/#\/schema\/person\/7a6c073117bef597a6afe22fb1081f3b","name":"mastervincehypnosis@hotmail.com","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/mastervincehypnosis.com\/wp-content\/uploads\/2022\/11\/Patreon-Cover-image-avatar-2.png","url":"https:\/\/mastervincehypnosis.com\/wp-content\/uploads\/2022\/11\/Patreon-Cover-image-avatar-2.png","contentUrl":"https:\/\/mastervincehypnosis.com\/wp-content\/uploads\/2022\/11\/Patreon-Cover-image-avatar-2.png","width":1024,"height":1024,"caption":"mastervincehypnosis@hotmail.com"},"logo":{"@id":"https:\/\/mastervincehypnosis.com\/wp-content\/uploads\/2022\/11\/Patreon-Cover-image-avatar-2.png"},"sameAs":["http:\/\/mastervincehypnosis.com","https:\/\/www.facebook.com\/MasterVinceHypnosis","https:\/\/www.instagram.com\/mastervincehypnosis\/","https:\/\/www.pinterest.fr\/mastervincehypnosis\/","https:\/\/x.com\/https:\/\/twitter.com\/MVHypnosis","https:\/\/www.youtube.com\/@mastervincehypnosis"]}]}},"_links":{"self":[{"href":"https:\/\/mastervincehypnosis.com\/index.php?rest_route=\/wp\/v2\/pages\/4734","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mastervincehypnosis.com\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/mastervincehypnosis.com\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/mastervincehypnosis.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mastervincehypnosis.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=4734"}],"version-history":[{"count":69,"href":"https:\/\/mastervincehypnosis.com\/index.php?rest_route=\/wp\/v2\/pages\/4734\/revisions"}],"predecessor-version":[{"id":6119,"href":"https:\/\/mastervincehypnosis.com\/index.php?rest_route=\/wp\/v2\/pages\/4734\/revisions\/6119"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/mastervincehypnosis.com\/index.php?rest_route=\/wp\/v2\/media\/4746"}],"wp:attachment":[{"href":"https:\/\/mastervincehypnosis.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4734"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}