Sandbox Environment

Test your myVibe micro-applications in this secure sandbox. Write code, see results in real-time, and simulate device interactions.

Device Simulator

Vibration:
0%

JavaScript

HTML

Preview

// Console output will appear here
`; // Set iframe content const blob = new Blob([frameContent], { type: 'text/html' }); previewFrame.src = URL.createObjectURL(blob); } // Reset to default code function resetCode() { codeEditor.value = codeEditor.defaultValue; htmlEditor.value = htmlEditor.defaultValue; runCode(); } // Add message to console output function addConsoleMessage(message, type = 'log') { const line = document.createElement('div'); line.className = `console-line console-${type}`; line.textContent = message; consoleOutput.appendChild(line); consoleOutput.scrollTop = consoleOutput.scrollHeight; } // Handle iframe messages window.addEventListener('message', function(event) { if (event.data && event.data.type === 'console') { const { method, args } = event.data; addConsoleMessage(args.join(' '), method); } else if (event.data && event.data.type === 'vibration') { const { value } = event.data; vibrationIndicator.style.setProperty('--value', `${value}%`); vibrationValue.textContent = `${value}%`; } }); // Example code templates const examples = { basic: { js: `// Initialize the myVibe SDK const myVibeApp = new MyVibeSDK.App({ name: "Basic Vibration Demo", version: "1.0.0", permissions: ["vibration"] }); // Connect to device and control vibration myVibeApp.onDeviceConnected((device) => { console.log(`Connected to ${device.name}`); console.log(`Battery level: ${device.batteryLevel}%`); // Set up intensity slider const intensitySlider = document.getElementById('intensity-slider'); const intensityValue = document.getElementById('intensity-value'); intensitySlider.addEventListener('input', () => { const value = intensitySlider.value; intensityValue.textContent = value + '%'; myVibeApp.setVibration(parseInt(value)); }); // Set up buttons document.getElementById('stop-btn').addEventListener('click', () => { myVibeApp.stopVibration(); intensitySlider.value = 0; intensityValue.textContent = '0%'; }); document.getElementById('max-btn').addEventListener('click', () => { myVibeApp.setVibration(100); intensitySlider.value = 100; intensityValue.textContent = '100%'; }); }); // Simulate device connection after page loads setTimeout(() => { myVibeApp._simulateDeviceConnected({ name: "MyVibe Test Device", id: "TEST-12345", batteryLevel: 85 }); }, 1000);`, html: `

Basic Vibration Control

Device: Not connected

Battery: -

0%
` }, patterns: { js: `// Initialize the myVibe SDK const myVibeApp = new MyVibeSDK.App({ name: "Pattern Creation Demo", version: "1.0.0", permissions: ["vibration", "patterns"] }); // Create patterns const patterns = { wave: myVibeApp.createPattern({ name: "Wave", steps: [ { intensity: 20, duration: 300 }, { intensity: 40, duration: 300 }, { intensity: 60, duration: 300 }, { intensity: 80, duration: 300 }, { intensity: 100, duration: 500 }, { intensity: 80, duration: 300 }, { intensity: 60, duration: 300 }, { intensity: 40, duration: 300 }, { intensity: 20, duration: 300 } ], repeat: 3 }), pulse: myVibeApp.createPattern({ name: "Pulse", steps: [ { intensity: 100, duration: 500 }, { intensity: 0, duration: 300 }, { intensity: 100, duration: 500 }, { intensity: 0, duration: 300 } ], repeat: 5 }), escalate: myVibeApp.createPattern({ name: "Escalate", steps: [ { intensity: 20, duration: 1000 }, { intensity: 40, duration: 1000 }, { intensity: 60, duration: 1000 }, { intensity: 80, duration: 1000 }, { intensity: 100, duration: 2000 } ], repeat: 2 }), random: myVibeApp.createPattern({ name: "Random", steps: [ { intensity: 40, duration: 400 }, { intensity: 10, duration: 200 }, { intensity: 70, duration: 500 }, { intensity: 30, duration: 300 }, { intensity: 100, duration: 800 }, { intensity: 20, duration: 400 }, { intensity: 60, duration: 600 } ], repeat: 3 }) }; // Connect to device and set up UI myVibeApp.onDeviceConnected((device) => { console.log(`Connected to ${device.name}`); updateDeviceInfo(device); // Set up pattern buttons document.querySelectorAll('.pattern-btn').forEach(btn => { btn.addEventListener('click', () => { const patternName = btn.dataset.pattern; playPattern(patternName); }); }); // Set up control buttons document.getElementById('stop-btn').addEventListener('click', () => { myVibeApp.stopPattern(); updateActivePattern(null); }); document.getElementById('pause-btn').addEventListener('click', () => { myVibeApp.pausePattern(); document.getElementById('pause-btn').style.display = 'none'; document.getElementById('resume-btn').style.display = 'inline-block'; }); document.getElementById('resume-btn').addEventListener('click', () => { myVibeApp.resumePattern(); document.getElementById('resume-btn').style.display = 'none'; document.getElementById('pause-btn').style.display = 'inline-block'; }); }); // Handle pattern completion myVibeApp.onPatternComplete(pattern => { console.log(`Pattern "${pattern.name}" completed`); updateActivePattern(null); }); // Play a pattern by name function playPattern(patternName) { const pattern = patterns[patternName]; if (!pattern) return; console.log(`Playing pattern: ${pattern.name}`); myVibeApp.playPattern(pattern); updateActivePattern(patternName); // Show pause button, hide resume button document.getElementById('pause-btn').style.display = 'inline-block'; document.getElementById('resume-btn').style.display = 'none'; } // Update UI to show active pattern function updateActivePattern(patternName) { document.querySelectorAll('.pattern-btn').forEach(btn => { if (btn.dataset.pattern === patternName) { btn.classList.add('active'); } else { btn.classList.remove('active'); } }); const activePattern = document.getElementById('active-pattern'); activePattern.textContent = patternName ? patterns[patternName].name : 'None'; } // Update device info in UI function updateDeviceInfo(device) { document.getElementById('device-name').textContent = device.name; document.getElementById('battery-level').textContent = `${device.batteryLevel}%`; } // Simulate device connection after page loads setTimeout(() => { myVibeApp._simulateDeviceConnected({ name: "MyVibe Test Device", id: "TEST-12345", batteryLevel: 85 }); }, 1000);`, html: `

Vibration Pattern Demo

Device: Not connected

Battery: -

Active Pattern: None

Select Pattern

` }, events: { js: `// Initialize the myVibe SDK const myVibeApp = new MyVibeSDK.App({ name: "Device Events Demo", version: "1.0.0", permissions: ["vibration", "patterns"] }); // Set up event logging function logEvent(eventName, data) { const eventsLog = document.getElementById('events-log'); const timestamp = new Date().toLocaleTimeString(); const eventItem = document.createElement('div'); eventItem.className = 'event-item'; const eventHeader = document.createElement('div'); eventHeader.className = 'event-header'; eventHeader.innerHTML = `${eventName} ${timestamp}`; const eventData = document.createElement('div'); eventData.className = 'event-data'; eventData.textContent = typeof data === 'object' ? JSON.stringify(data) : data; eventItem.appendChild(eventHeader); eventItem.appendChild(eventData); eventsLog.appendChild(eventItem); // Auto scroll to bottom eventsLog.scrollTop = eventsLog.scrollHeight; } // Register event handlers myVibeApp.onDeviceConnected(device => { logEvent('Device Connected', device); updateUI(); }); myVibeApp.onDeviceDisconnected(device => { logEvent('Device Disconnected', device); updateUI(); }); myVibeApp.onBatteryLow(device => { logEvent('Battery Low', device); }); myVibeApp.onPatternComplete(pattern => { logEvent('Pattern Complete', pattern); }); // Set up UI Controls function updateUI() { const device = myVibeApp.getDeviceInfo(); const deviceConnected = device?.name && device.name !== 'No device connected'; // Update device info document.getElementById('device-name').textContent = deviceConnected ? device.name : 'Not connected'; document.getElementById('battery-level').textContent = deviceConnected ? `${device.batteryLevel}%` : '-'; // Update button states document.getElementById('connect-btn').disabled = deviceConnected; document.getElementById('disconnect-btn').disabled = !deviceConnected; document.getElementById('battery-low-btn').disabled = !deviceConnected; document.getElementById('vibrate-btn').disabled = !deviceConnected; document.getElementById('pattern-btn').disabled = !deviceConnected; } // Set up button actions document.addEventListener('DOMContentLoaded', () => { document.getElementById('connect-btn').addEventListener('click', () => { myVibeApp._simulateDeviceConnected({ name: "MyVibe Test Device", id: "TEST-" + Math.floor(Math.random() * 10000), batteryLevel: Math.floor(Math.random() * 30) + 70 // 70-99% }); }); document.getElementById('disconnect-btn').addEventListener('click', () => { myVibeApp._simulateDeviceDisconnected(); }); document.getElementById('battery-low-btn').addEventListener('click', () => { myVibeApp._simulateBatteryLow(); }); document.getElementById('vibrate-btn').addEventListener('click', () => { const intensity = 70; myVibeApp.setVibration(intensity); logEvent('Vibration Changed', `Intensity: ${intensity}%`); // Auto stop after 2 seconds setTimeout(() => { myVibeApp.stopVibration(); logEvent('Vibration Stopped', ''); }, 2000); }); document.getElementById('pattern-btn').addEventListener('click', () => { const pattern = myVibeApp.createPattern({ name: "Test Pattern", steps: [ { intensity: 100, duration: 500 }, { intensity: 0, duration: 300 }, { intensity: 100, duration: 500 } ], repeat: 2 }); myVibeApp.playPattern(pattern); logEvent('Pattern Started', pattern); }); document.getElementById('clear-btn').addEventListener('click', () => { document.getElementById('events-log').innerHTML = ''; }); updateUI(); });`, html: `

Device Events Demo

Device: Not connected

Battery: -

Connection

Control

Event Log

` }, ui: { js: `// Initialize the myVibe SDK const myVibeApp = new MyVibeSDK.App({ name: "Custom UI Demo", version: "1.0.0", permissions: ["vibration", "patterns"] }); // Create pattern types const patterns = { slow: { rampUp: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100], duration: 400, // ms per step holdTime: 1000, // ms at peak rampDown: [90, 80, 70, 60, 50, 40, 30, 20, 10, 0], restTime: 2000 // ms between cycles }, medium: { rampUp: [20, 40, 60, 80, 100], duration: 300, holdTime: 800, rampDown: [80, 60, 40, 20, 0], restTime: 1500 }, fast: { rampUp: [25, 50, 75, 100], duration: 200, holdTime: 600, rampDown: [75, 50, 25, 0], restTime: 1000 }, pulse: { rampUp: [100], duration: 150, holdTime: 150, rampDown: [0], restTime: 150 } }; // Generate SDK pattern from settings function createSDKPattern(settings, cycles = 3) { const steps = []; // Add ramp up steps settings.rampUp.forEach(intensity => { steps.push({ intensity, duration: settings.duration }); }); // Add hold at peak step if (settings.holdTime > 0) { steps.push({ intensity: settings.rampUp[settings.rampUp.length - 1], duration: settings.holdTime }); } // Add ramp down steps settings.rampDown.forEach(intensity => { steps.push({ intensity, duration: settings.duration }); }); // Add rest step if (settings.restTime > 0) { steps.push({ intensity: 0, duration: settings.restTime }); } return myVibeApp.createPattern({ name: "Custom Pattern", steps, repeat: cycles }); } // UI Control variables let currentPatternKey = 'slow'; let currentIntensity = 70; let currentCycles = 3; let isPlaying = false; // Set up UI Controls myVibeApp.onDeviceConnected(device => { console.log(`Connected to ${device.name}`); updateDeviceInfo(device); enableControls(); }); // Update device info in UI function updateDeviceInfo(device) { document.getElementById('device-name').textContent = device.name; document.getElementById('device-battery').textContent = `${device.batteryLevel}%`; document.querySelector('.device-status').classList.add('connected'); } // Enable UI controls after device connection function enableControls() { document.querySelectorAll('.control').forEach(control => { control.disabled = false; }); // Update displays updatePatternDisplay(); updateIntensityDisplay(); updateCyclesDisplay(); } // UI update functions function updatePatternDisplay() { document.querySelectorAll('.pattern-btn').forEach(btn => { btn.classList.toggle('active', btn.dataset.pattern === currentPatternKey); }); document.getElementById('pattern-display').textContent = currentPatternKey.charAt(0).toUpperCase() + currentPatternKey.slice(1); // Update pattern visualization const pattern = patterns[currentPatternKey]; updatePatternVisualization(pattern); } function updatePatternVisualization(pattern) { const canvas = document.getElementById('pattern-canvas'); const ctx = canvas.getContext('2d'); const width = canvas.width; const height = canvas.height; // Clear canvas ctx.clearRect(0, 0, width, height); // Set styles ctx.lineWidth = 3; ctx.lineJoin = 'round'; ctx.lineCap = 'round'; ctx.strokeStyle = '#ff5e85'; // Calculate total pattern duration const rampUpTime = pattern.duration * pattern.rampUp.length; const rampDownTime = pattern.duration * pattern.rampDown.length; const totalTime = rampUpTime + pattern.holdTime + rampDownTime + pattern.restTime; // Calculate scale factors const timeScale = width / totalTime; // Start drawing ctx.beginPath(); ctx.moveTo(0, height); let currentX = 0; // Draw ramp up pattern.rampUp.forEach(intensity => { const y = height - (intensity / 100 * height); ctx.lineTo(currentX, y); currentX += pattern.duration * timeScale; }); // Draw hold if (pattern.holdTime > 0) { ctx.lineTo(currentX, height - (pattern.rampUp[pattern.rampUp.length - 1] / 100 * height)); currentX += pattern.holdTime * timeScale; } // Draw ramp down pattern.rampDown.forEach(intensity => { const y = height - (intensity / 100 * height); ctx.lineTo(currentX, y); currentX += pattern.duration * timeScale; }); // Draw rest if (pattern.restTime > 0) { ctx.lineTo(currentX, height); currentX += pattern.restTime * timeScale; } ctx.stroke(); // Draw cycle repeats ctx.setLineDash([5, 5]); ctx.strokeStyle = '#ccc'; ctx.beginPath(); const cycleWidth = totalTime * timeScale; for (let i = 1; i < currentCycles; i++) { const x = i * cycleWidth; ctx.moveTo(x, 0); ctx.lineTo(x, height); } ctx.stroke(); ctx.setLineDash([]); } function updateIntensityDisplay() { document.getElementById('intensity-slider').value = currentIntensity; document.getElementById('intensity-display').textContent = `${currentIntensity}%`; } function updateCyclesDisplay() { document.getElementById('cycles-display').textContent = currentCycles; } // Play the current pattern function playPattern() { if (isPlaying) { myVibeApp.stopPattern(); document.getElementById('play-btn').textContent = 'Play'; document.getElementById('play-btn').classList.remove('active'); isPlaying = false; return; } // Get current pattern settings and apply intensity modifier const basePattern = patterns[currentPatternKey]; const modifiedPattern = { rampUp: basePattern.rampUp.map(v => Math.round(v * currentIntensity / 100)), duration: basePattern.duration, holdTime: basePattern.holdTime, rampDown: basePattern.rampDown.map(v => Math.round(v * currentIntensity / 100)), restTime: basePattern.restTime }; // Create and play the pattern const sdkPattern = createSDKPattern(modifiedPattern, currentCycles); myVibeApp.playPattern(sdkPattern); document.getElementById('play-btn').textContent = 'Stop'; document.getElementById('play-btn').classList.add('active'); isPlaying = true; console.log(`Playing ${currentPatternKey} pattern at ${currentIntensity}% intensity for ${currentCycles} cycles`); } // Set up event listeners document.addEventListener('DOMContentLoaded', () => { // Pattern selection buttons document.querySelectorAll('.pattern-btn').forEach(btn => { btn.addEventListener('click', () => { currentPatternKey = btn.dataset.pattern; updatePatternDisplay(); }); }); // Intensity slider document.getElementById('intensity-slider').addEventListener('input', (e) => { currentIntensity = parseInt(e.target.value); updateIntensityDisplay(); }); // Cycle controls document.getElementById('cycles-minus').addEventListener('click', () => { if (currentCycles > 1) { currentCycles--; updateCyclesDisplay(); updatePatternVisualization(patterns[currentPatternKey]); } }); document.getElementById('cycles-plus').addEventListener('click', () => { if (currentCycles < 10) { currentCycles++; updateCyclesDisplay(); updatePatternVisualization(patterns[currentPatternKey]); } }); // Play button document.getElementById('play-btn').addEventListener('click', playPattern); // Simulate device connection after page loads setTimeout(() => { myVibeApp._simulateDeviceConnected({ name: "MyVibe Test Device", id: "TEST-12345", batteryLevel: 85 }); }, 1000); // Pattern complete handler myVibeApp.onPatternComplete(() => { console.log('Pattern completed'); document.getElementById('play-btn').textContent = 'Play'; document.getElementById('play-btn').classList.remove('active'); isPlaying = false; }); // Initialize visualization updatePatternVisualization(patterns[currentPatternKey]); }); `, html: `

Vibration Pattern Controller

Device: Not connected
Battery: -

Pattern

Selected: Slow

Intensity

70%

Cycles

3
` } }; // Event listeners runBtn.addEventListener('click', runCode); resetBtn.addEventListener('click', resetCode); exampleSelect.addEventListener('change', function() { const selection = this.value; if (selection && examples[selection]) { codeEditor.value = examples[selection].js; htmlEditor.value = examples[selection].html; runCode(); this.value = ''; } }); // Initialize runCode(); });