Preview
Console Output
Console ready. JavaScript output will appear here.
Basic HTML Page
Simple HTML structure with CSS and JavaScript
Bootstrap Template
Responsive layout with Bootstrap framework
React Component
Simple React component with hooks
Vue.js App
Vue.js application with component structure
Canvas Animation
HTML5 Canvas with JavaScript animation
API Integration
Fetch API with error handling
`, css: `body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); margin: 0; padding: 0; min-height: 100vh; } button { background: #4CAF50; color: white; border: none; padding: 10px 20px; margin: 5px; border-radius: 4px; cursor: pointer; font-size: 16px; } button:hover { background: #45a049; }`, js: `// React component is embedded in HTML` }, canvas: { html: ` Canvas Animation
`, css: `body { font-family: Arial, sans-serif; background: #1a1a1a; color: white; text-align: center; margin: 0; padding: 20px; } canvas { border: 2px solid #333; border-radius: 8px; background: #000; } button { background: #4CAF50; color: white; border: none; padding: 10px 20px; margin: 5px; border-radius: 4px; cursor: pointer; font-size: 16px; } button:hover { background: #45a049; }`, js: `const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); const startBtn = document.getElementById('startBtn'); const stopBtn = document.getElementById('stopBtn'); let animationId; let particles = []; class Particle { constructor() { this.x = Math.random() * canvas.width; this.y = Math.random() * canvas.height; this.vx = (Math.random() - 0.5) * 4; this.vy = (Math.random() - 0.5) * 4; this.size = Math.random() * 3 + 1; this.color = \`hsl(\${Math.random() * 360}, 70%, 50%)\`; } update() { this.x += this.vx; this.y += this.vy; if (this.x < 0 || this.x > canvas.width) this.vx *= -1; if (this.y < 0 || this.y > canvas.height) this.vy *= -1; } draw() { ctx.beginPath(); ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); ctx.fillStyle = this.color; ctx.fill(); } } function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height); particles.forEach(particle => { particle.update(); particle.draw(); }); animationId = requestAnimationFrame(animate); } startBtn.addEventListener('click', () => { particles = Array.from({length: 50}, () => new Particle()); animate(); }); stopBtn.addEventListener('click', () => { cancelAnimationFrame(animationId); ctx.clearRect(0, 0, canvas.width, canvas.height); });` } }; } setupEventListeners() { // Core functionality this.runBtn.addEventListener('click', () => this.updatePreview()); this.refreshBtn.addEventListener('click', () => this.updatePreview()); this.formatBtn.addEventListener('click', () => this.formatCode()); this.exportBtn.addEventListener('click', () => this.exportProject()); // File operations this.saveBtn.addEventListener('click', () => this.saveProject()); this.loadBtn.addEventListener('click', () => this.loadProject()); this.resetBtn.addEventListener('click', () => this.resetToDefault()); // UI controls this.templatesBtn.addEventListener('click', () => this.toggleTemplates()); this.consoleToggle.addEventListener('click', () => this.toggleConsole()); this.clearConsole.addEventListener('click', () => this.clearConsoleOutput()); // Tab switching this.tabs.forEach(tab => { tab.addEventListener('click', () => this.switchTab(tab)); }); // Device selector this.deviceBtns.forEach(btn => { btn.addEventListener('click', () => this.selectDevice(btn)); }); // Template selection document.querySelectorAll('.template-item').forEach(item => { item.addEventListener('click', () => this.loadTemplate(item)); }); // Auto-update with debounce let updateTimeout; [this.htmlEditor, this.cssEditor, this.jsEditor].forEach(editor => { editor.addEventListener('input', () => { clearTimeout(updateTimeout); updateTimeout = setTimeout(() => { this.updatePreview(); this.updateLineNumbers(); this.updateStats(); }, 1000); this.statusText.textContent = 'Changes detected...'; }); }); // Setup resizer this.setupResizer(); } initializeFeatures() { // Initialize console capture this.setupConsoleCapture(); // Initialize line numbers this.updateLineNumbers(); // Initialize stats this.updateStats(); } switchTab(activeTab) { const tabName = activeTab.getAttribute('data-tab'); // Update active tab this.tabs.forEach(t => t.classList.remove('active')); activeTab.classList.add('active'); // Show corresponding editor document.querySelectorAll('.editor-wrapper').forEach(wrapper => { wrapper.style.display = 'none'; }); const activeWrapper = document.querySelector(`#${tabName}Editor`).parentElement; activeWrapper.style.display = 'block'; // Update line numbers for active editor this.updateLineNumbers(); } updatePreview() { try { const html = this.htmlEditor.value; const css = this.cssEditor.value; const js = this.jsEditor.value; // Create a complete HTML document with console capture /* eslint-disable */ const previewContent = '' + '' + '' + '' + '' + '' + '' + html + '' + '' + ''; // Update iframe this.preview.srcdoc = previewContent; this.statusText.textContent = 'Preview updated'; this.previewStatus.className = 'status-indicator success'; } catch (error) { console.error('Preview update error:', error); this.statusText.textContent = 'Preview error: ' + error.message; this.previewStatus.className = 'status-indicator error'; } } setupConsoleCapture() { window.addEventListener('message', (event) => { if (event.data.type === 'console') { this.addConsoleMessage(event.data.level, event.data.message); } }); } addConsoleMessage(level, message) { const timestamp = new Date().toLocaleTimeString(); const logElement = document.createElement('div'); logElement.className = `console-log console-${level}`; logElement.innerHTML = `[${timestamp}] ${message}`; this.consoleContent.appendChild(logElement); this.consoleContent.scrollTop = this.consoleContent.scrollHeight; } toggleConsole() { this.consolePanel.style.display = this.consolePanel.style.display === 'none' ? 'flex' : 'none'; } clearConsoleOutput() { this.consoleContent.innerHTML = '
Console cleared.
'; } toggleTemplates() { this.templateLibrary.style.display = this.templateLibrary.style.display === 'none' ? 'block' : 'none'; } loadTemplate(templateItem) { const templateName = templateItem.getAttribute('data-template'); const template = this.templates[templateName]; if (template) { this.htmlEditor.value = template.html; this.cssEditor.value = template.css; this.jsEditor.value = template.js; this.updatePreview(); this.updateLineNumbers(); this.updateStats(); this.templateLibrary.style.display = 'none'; this.statusText.textContent = `Loaded ${templateName} template`; } } selectDevice(deviceBtn) { this.deviceBtns.forEach(btn => btn.classList.remove('active')); deviceBtn.classList.add('active'); const device = deviceBtn.getAttribute('data-device'); const preview = this.preview; switch(device) { case 'mobile': preview.style.width = '375px'; preview.style.height = '667px'; preview.style.margin = '0 auto'; break; case 'tablet': preview.style.width = '768px'; preview.style.height = '1024px'; preview.style.margin = '0 auto'; break; case 'desktop': default: preview.style.width = '100%'; preview.style.height = '100%'; preview.style.margin = '0'; break; } } formatCode() { // Simple code formatting (in a real app, you'd use a proper formatter) const activeEditor = this.getActiveEditor(); if (activeEditor) { const formatted = this.formatCodeContent(activeEditor.value, this.getActiveLanguage()); activeEditor.value = formatted; this.updatePreview(); this.statusText.textContent = 'Code formatted'; } } getActiveEditor() { const activeTab = document.querySelector('.tab.active'); const tabName = activeTab.getAttribute('data-tab'); return document.getElementById(tabName + 'Editor'); } getActiveLanguage() { const activeTab = document.querySelector('.tab.active'); return activeTab.getAttribute('data-tab'); } formatCodeContent(code, language) { // Basic formatting - in a real app, use a proper formatter switch(language) { case 'html': return code.replace(/>\n<'); case 'css': return code.replace(/;/g, ';\n').replace(/\{/g, ' {\n').replace(/\}/g, '\n}\n'); case 'js': return code.replace(/;/g, ';\n').replace(/\{/g, ' {\n').replace(/\}/g, '\n}\n'); default: return code; } } updateLineNumbers() { const activeEditor = this.getActiveEditor(); if (!activeEditor) return; const lineNumbersElement = document.getElementById(this.getActiveLanguage() + 'LineNumbers'); if (!lineNumbersElement) return; const lines = activeEditor.value.split('\n'); const lineNumbers = lines.map((_, index) => index + 1).join('\n'); lineNumbersElement.textContent = lineNumbers; } updateStats() { const activeEditor = this.getActiveEditor(); if (!activeEditor) return; const lines = activeEditor.value.split('\n').length; const chars = activeEditor.value.length; this.lineCount.textContent = `Lines: ${lines}`; this.charCount.textContent = `Chars: ${chars}`; } saveProject() { const codeData = { html: this.htmlEditor.value, css: this.cssEditor.value, js: this.jsEditor.value, timestamp: new Date().toISOString(), version: '2.0' }; const dataStr = JSON.stringify(codeData, null, 2); const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr); const linkElement = document.createElement('a'); linkElement.setAttribute('href', dataUri); linkElement.setAttribute('download', 'playground-project.json'); linkElement.click(); this.statusText.textContent = 'Project saved successfully'; } loadProject() { const input = document.createElement('input'); input.type = 'file'; input.accept = '.json'; input.onchange = e => { const file = e.target.files[0]; const reader = new FileReader(); reader.onload = event => { try { const codeData = JSON.parse(event.target.result); this.htmlEditor.value = codeData.html || ''; this.cssEditor.value = codeData.css || ''; this.jsEditor.value = codeData.js || ''; this.updatePreview(); this.updateLineNumbers(); this.updateStats(); this.statusText.textContent = 'Project loaded successfully'; } catch (error) { alert('Error loading file: Invalid format'); this.statusText.textContent = 'Error loading file'; } }; reader.readAsText(file); }; input.click(); } exportProject() { const html = this.htmlEditor.value; const css = this.cssEditor.value; const js = this.jsEditor.value; /* eslint-disable */ const exportContent = '' + '' + '' + '' + '' + 'Exported Project' + '' + '' + '' + html + '' + '' + ''; const dataUri = 'data:text/html;charset=utf-8,'+ encodeURIComponent(exportContent); const linkElement = document.createElement('a'); linkElement.setAttribute('href', dataUri); linkElement.setAttribute('download', 'exported-project.html'); linkElement.click(); this.statusText.textContent = 'Project exported as HTML'; } resetToDefault() { if (confirm('Are you sure you want to reset all code to default?')) { const basicTemplate = this.templates.basic; this.htmlEditor.value = basicTemplate.html; this.cssEditor.value = basicTemplate.css; this.jsEditor.value = basicTemplate.js; this.updatePreview(); this.updateLineNumbers(); this.updateStats(); this.statusText.textContent = 'Reset to default'; } } setupResizer() { let isResizing = false; this.resizer.addEventListener('mousedown', (e) => { isResizing = true; document.body.style.cursor = 'col-resize'; document.addEventListener('mousemove', this.handleMouseMove); document.addEventListener('mouseup', this.stopResize); }); this.handleMouseMove = (e) => { if (!isResizing) return; const containerRect = document.querySelector('.editor-container').getBoundingClientRect(); const containerWidth = containerRect.width; const newLeftWidth = ((e.clientX - containerRect.left) / containerWidth) * 100; const newRightWidth = 100 - newLeftWidth; document.querySelector('.code-panel').style.flex = `0 0 ${newLeftWidth}%`; document.querySelector('.preview-panel').style.flex = `0 0 ${newRightWidth}%`; }; this.stopResize = () => { isResizing = false; document.body.style.cursor = 'default'; document.removeEventListener('mousemove', this.handleMouseMove); document.removeEventListener('mouseup', this.stopResize); }; } } // Initialize the playground when DOM is ready document.addEventListener('DOMContentLoaded', () => { new AdvancedPlayground(); });