Try it! - ///_hyperscript

Playground

Editor
Preview
Console
>
Ready — Ctrl+Enter to run, Ctrl+Space for autocomplete
', hello: '

Hello

\n\n', colors: '

Color Cycle

\n\n
Watch me change!
', }; // ============================================================ // Hyperscript overlay mode for CodeMirror // ============================================================ var HS_KEYWORDS = [ "on", "init", "immediately", "end", "if", "else", "then", "set", "get", "put", "into", "before", "after", "add", "remove", "toggle", "trigger", "send", "take", "log", "call", "wait", "settle", "repeat", "for", "in", "while", "until", "from", "to", "by", "increment", "decrement", "default", "transition", "fetch", "throw", "return", "exit", "halt", "async", "tell", "go", "hide", "show", "with", "when", "changes", "live", "bind", "and", "or", "not", "no", "is", "am", "the", "a", "an", "of", "closest", "next", "previous", "first", "last", "random", "as", "my", "me", "it", "its", "you", "your", "yourself", "result", "event", "target", "detail", "sender", "body", "true", "false", "null", "undefined", "global", "element", "local", "dom", "live", "watch", "behavior", "install", "def", "js", "make", "measure", "append", "pick", "match", "eventsource", "socket", ]; var HS_KEYWORD_SET = new Set(HS_KEYWORDS); CodeMirror.defineMode("html-hyperscript", function (config) { return CodeMirror.overlayMode( CodeMirror.getMode(config, "htmlmixed"), { token: function (stream) { if (stream.match("--")) { stream.skipToEnd(); return "hs-comment"; } if (stream.match(/^\^@[a-zA-Z_][\w-]*/)) return "hs-symbol-inherited-attr"; if (stream.match(/^\^[a-zA-Z_]\w*/)) return "hs-symbol-dom"; if (stream.match(/^\$[a-zA-Z_]\w*/)) return "hs-symbol-global"; if (stream.match(/^:[a-zA-Z_]\w*/)) return "hs-symbol-element"; if (stream.match(/^[a-zA-Z_]\w*/)) { var word = stream.current(); if (HS_KEYWORD_SET.has(word)) return "hs-keyword"; return null; } if (stream.match(/^#[a-zA-Z_][\w-]*/)) return "hs-symbol-element"; if (stream.match(/^\.[a-zA-Z_][\w-]*/)) return "hs-symbol-dom"; stream.next(); return null; } } ); }); // ============================================================ // Linting // ============================================================ function lintHyperscript(text) { var errors = []; var attrRegex = /_\s*=\s*"([^"]*)"/g; var match; var lines = text.split("\n"); // Build list of template tag ranges to skip var templateRanges = []; var tmplRegex = /', tmplMatch.index); if (closeIdx !== -1) { templateRanges.push([tmplMatch.index, closeIdx + ''.length]); } } while ((match = attrRegex.exec(text)) !== null) { // Skip _ attributes inside

' ); doc.close(); setTimeout(function () { var status = document.getElementById('status'); if (lastErrors.length > 0) { status.innerHTML = '' + lastErrors.length + ' error(s)'; } else { status.innerHTML = 'Updated (' + new Date().toLocaleTimeString() + ')'; } }, 200); } window._playgroundError = function (msg) { lastErrors.push(msg); consoleAppend('error', msg); }; window._playgroundConsole = function (level, msg) { consoleAppend(level, msg); }; // ============================================================ // Console // ============================================================ var consoleOutput = document.getElementById('console-output'); var consoleInput = document.getElementById('console-input'); var consoleHistory = []; var historyIndex = -1; var replLocals = {}; var BLOCK_OPENERS = /(? closes; } function consoleAppend(cls, text) { var el = document.createElement('div'); el.className = cls; if (cls === 'separator') { el.innerHTML = ' '; } else { el.textContent = text; } consoleOutput.appendChild(el); consoleOutput.scrollTop = consoleOutput.scrollHeight; } function consoleEval(input) { var lines = input.split('\n'); consoleAppend('log', '> ' + lines[0]); for (var i = 1; i < lines.length; i++) { consoleAppend('log', '... ' + lines[i]); } var iframe = document.getElementById('preview'); var hs = iframe.contentWindow && iframe.contentWindow._hyperscript; if (!hs) { consoleAppend('error', 'No preview running - press Run first'); return; } try { var result = hs(input, { locals: replLocals }); if (result !== undefined) { var display; try { display = typeof result === 'object' ? JSON.stringify(result) : String(result); } catch (e) { display = String(result); } consoleAppend('result', display); } } catch (e) { consoleAppend('error', e.message ? e.message.split('\n')[0] : String(e)); } } function autoSizeInput() { consoleInput.style.height = '18px'; consoleInput.style.height = consoleInput.scrollHeight + 'px'; } consoleInput.addEventListener('input', autoSizeInput); consoleInput.addEventListener('keydown', function (e) { if (e.key === 'Enter') { var src = this.value.trim(); if (!src) return; if (needsMoreInput(src) && !e.shiftKey) { // Block incomplete - insert newline naturally return; } if (!needsMoreInput(src)) { // Complete - submit e.preventDefault(); var input = this.value; this.value = ''; autoSizeInput(); consoleHistory.push(input); historyIndex = consoleHistory.length; consoleEval(input); } // Shift+Enter always inserts newline (default behavior) } else if (e.key === 'ArrowUp' && this.selectionStart === 0 && !this.value.includes('\n')) { e.preventDefault(); if (historyIndex > 0) { historyIndex--; this.value = consoleHistory[historyIndex]; autoSizeInput(); } } else if (e.key === 'ArrowDown' && this.selectionStart === this.value.length && !this.value.includes('\n')) { e.preventDefault(); if (historyIndex < consoleHistory.length - 1) { historyIndex++; this.value = consoleHistory[historyIndex]; autoSizeInput(); } else { historyIndex = consoleHistory.length; this.value = ''; autoSizeInput(); } } }); // ============================================================ // Controls // ============================================================ document.getElementById('run-btn').addEventListener('click', run); document.getElementById('clear-btn').addEventListener('click', function () { cm.setValue(''); var doc = document.getElementById('preview').contentDocument; doc.open(); doc.write('

'); doc.close(); consoleOutput.innerHTML = ''; document.getElementById('status').textContent = 'Cleared'; }); document.getElementById('share-btn').addEventListener('click', function () { var btn = this; var compressed = LZString.compressToEncodedURIComponent(cm.getValue()); var url = window.location.origin + window.location.pathname + '?src=' + compressed; navigator.clipboard.writeText(url).then(function () { showToast('Share URL copied!', btn); }); }); function showToast(msg, anchor) { var existing = document.querySelector('.toast'); if (existing) existing.remove(); anchor.style.position = 'relative'; var toast = document.createElement('div'); toast.className = 'toast'; toast.textContent = msg; anchor.appendChild(toast); requestAnimationFrame(function () { toast.classList.add('show'); }); setTimeout(function () { toast.classList.remove('show'); toast.addEventListener('transitionend', function () { toast.remove(); }); }, 1500); } document.getElementById('examples').addEventListener('change', function () { var val = this.value; if (!val) return; this.value = ''; if (BUILTIN_EXAMPLES[val]) { cm.setValue(BUILTIN_EXAMPLES[val]); run(); return; } // Fetch from /playground/ directory fetch('/playground/' + val) .then(function (r) { return r.text(); }) .then(function (text) { cm.setValue(text); run(); }) .catch(function () { document.getElementById('status').innerHTML = 'Failed to load example'; }); }); // ============================================================ // Resizable panes // ============================================================ (function () { var resizeH = document.getElementById('resize-h'); var resizeV = document.getElementById('resize-v'); var panes = document.querySelector('.playground-panes'); var editor = document.querySelector('.pane-editor'); var preview = document.querySelector('.pane-preview'); var previewFrame = document.getElementById('preview'); var consolePane = document.querySelector('.pane-console'); function startDrag(handle, onMove, onEnd) { handle.classList.add('dragging'); // Cover iframe to prevent it from eating pointer events previewFrame.style.pointerEvents = 'none'; document.addEventListener('mousemove', onMove); document.addEventListener('mouseup', function up() { handle.classList.remove('dragging'); previewFrame.style.pointerEvents = ''; document.removeEventListener('mousemove', onMove); document.removeEventListener('mouseup', up); if (onEnd) onEnd(); }); } // Horizontal: editor | preview resizeH.addEventListener('mousedown', function (e) { e.preventDefault(); var panesRect = panes.getBoundingClientRect(); startDrag(resizeH, function (e) { var x = e.clientX - panesRect.left; var pct = (x / panesRect.width) * 100; pct = Math.max(15, Math.min(85, pct)); editor.style.flex = 'none'; editor.style.width = pct + '%'; preview.style.flex = '1'; }, function () { cm.refresh(); }); }); // Vertical: preview iframe | console resizeV.addEventListener('mousedown', function (e) { e.preventDefault(); var previewRect = preview.getBoundingClientRect(); startDrag(resizeV, function (e) { var y = e.clientY - previewRect.top; // Subtract the pane-label height (~24px) var available = previewRect.height - 24; var framePct = ((y - 24) / available) * 100; framePct = Math.max(10, Math.min(90, framePct)); previewFrame.style.flex = 'none'; previewFrame.style.height = framePct + '%'; consolePane.style.flex = '1'; }); }); })(); // Initial run setTimeout(run, 100);