Playgrounds

HTML, CSS & JS Playground

Write HTML, CSS or Less, and JavaScript with a live preview. Everything runs in a sandboxed frame in your browser.

Loading editors...

HTML

Styles

JavaScript

Preview

Markup and styles update as you type; the script runs on Run

Console

Anything your JavaScript logs shows up here.

What is an HTML, CSS and JS playground?

A front-end playground is three editors and a preview that keeps up with them. You put markup in one, styles in another, behaviour in the third, and the result renders beside them β€” markup and styles as you type, the script when you press Run. CodePen and JSFiddle made the shape familiar, and the reason it stuck is that a browser is already the runtime: there is nothing to install, nothing to build, and the gap between changing a value and seeing what it does is a few hundred milliseconds.

This one runs Monaco, the editor out of VS Code, in all three panes, so completion, bracket matching, and multiple cursors work the same as they do in your editor. The styles pane will take Less as well as CSS and compiles it before the preview sees it. The preview is a sandboxed frame, anything your script logs lands in the console panel underneath, and the Share button puts all three panes into a link. There is no account, and there is no server holding your work.

When to use it

The everyday case is a component you want to see before it goes anywhere near a repository. A card, a dropdown, a loading state, a piece of a form. Building it in the application means a dev server, a route to reach it, and whatever global styles the page brings with it; building it here means an empty document and only the rules you wrote. Once it behaves, the markup and the styles move across.

The second is trying a technique you have read about. Somebody posts that :has() solves a problem you have, or that a grid with subgrid collapses three wrappers into one. Reading that is not the same as knowing it, and the gap between the two is about ninety seconds here. The same goes for checking how a property behaves across a couple of values rather than trusting the one example in the article.

Then there is handing something to another person. A bug that only shows up with a particular combination of markup and CSS is hard to describe and easy to demonstrate, and a link that opens with the reproduction already in it saves the paragraph you were about to write. That covers bug reports, code review, and teaching, where being able to change one line and watch the layout move is most of the lesson. For JavaScript on its own the JS Playground is the smaller tool, the standalone Less compiler on this site handles a stylesheet with no page around it, and the CSS Prettifier tidies one without opening a playground at all.

How this tool works

The preview runs in an iframe whose sandbox attribute grants allow-scripts and nothing else, and the permission it withholds is the one that matters. Without allow-same-origin the frame runs at an opaque origin. Nothing inside it can reach the cookies or the storage that belong to this site, and nothing it does touches the page around it. That is what makes it safe to open a playground link somebody sent you. The whole frame is rebuilt on every update rather than patched, so a timer or a listener from the previous version of your code cannot survive into the next one.

Markup and styles are debounced by 400 milliseconds: the preview waits for you to pause rather than rebuilding on every keystroke. The script pane is different, and deliberately so. It runs when you press Run, and not before. This preview shares the page's thread, so a loop that never yields takes the whole tab with it β€” including the editor holding the loop β€” and no timeout can interrupt it. Re-running a script you are halfway through typing is the one way that happens without you asking for it, so it does not. The button shows a dot while the pane differs from what is running. Your markup goes into the frame as markup, which is what lets a script tag in the HTML pane pull a library off a CDN. The stylesheet and the script are handed over separately once the document exists, so a stylesheet containing </style> or a script containing </script> cannot end its own element early.

Switching the styles pane to Less compiles it with Less.js before the preview sees it, since browsers have never understood Less. Variables, mixins, nesting, guards, and the colour functions all work, because it is the same compiler the Less to CSS tool on this site uses. A stylesheet that will not compile reports the error above the pane and leaves the last one that did compile in the preview, so the page does not go white while you are halfway through typing a rule. Nothing has compiled yet on the very first render, so a shared link whose Less is broken opens unstyled with the error showing rather than with a previous version to fall back on. Compilation runs on the same debounce as the markup and styles. Switch the dropdown to CSS while the pane still holds Less and the pane says so, because the browser would silently drop every rule that uses a variable.

Output lands in the console panel below the preview, not in devtools. Every console method inside the frame is swapped for one that forwards its arguments out to this page, and uncaught errors and unsettled rejections travel the same route. Values are inspected rather than stringified: a Map arrives with its contents intact, and a structure that points back at itself reports the loop instead of throwing. Format runs each pane through the parser that matches it, Prettier for the markup and the script and the same routing the CSS Prettifier uses for the stylesheet, so a pane that will not parse is reported without stopping the other two.

What is not here is a build step. There is no bundler, no npm install, and no way to import a bare package name, because nothing is resolving specifiers for you. A script tag pointing at a CDN works, and so does a dynamic import from a URL, on the same terms any web page gets: the CDN has to send permissive CORS headers. That covers most of what a scratch file needs and none of what an application needs, which is the honest boundary of a tool like this.

Share puts all three panes and the styles-language setting into the URL as one encoded parameter, and nowhere else. Opening the link decodes it back into the three editors. Nothing is kept on this end, so there is no paste to expire, nothing to revoke, and nothing logging that you shared anything. The cost is length. Everything you wrote is in the address, so a big document makes an unwieldy one, and a few clients mangle how they show it even though the link still works.

Examples

  • A hover transition worth stealing

    Input
    /* Styles pane, in CSS mode */
    .btn {
      padding: 10px 22px;
      border: 0;
      border-radius: 999px;
      background: #1f6feb;
      color: #fff;
      transition: transform 0.18s ease, box-shadow 0.18s ease;
    }
    
    .btn:hover {
      transform: translateY(-2px);
      box-shadow: 0 6px 18px rgba(31, 111, 235, 0.35);
    }
    
    .btn:active {
      transform: translateY(0);
    }
    Output
    <!-- HTML pane -->
    <button class="btn" type="button">Hover me</button>

    Two panes, no script. The point of trying this here rather than in an application is that nothing else is on the page: no reset, no inherited font, no framework putting its own transition on the same element. What you see is what those nine declarations do on their own.

  • The same button, in Less

    Input
    @accent: #1f6feb;
    @lift: 2px;
    
    .btn {
      padding: 10px 22px;
      border: 0;
      border-radius: 999px;
      background: @accent;
      color: #fff;
      transition: transform 0.18s ease, box-shadow 0.18s ease;
    
      &:hover {
        transform: translateY(-@lift);
        box-shadow: 0 6px 18px fade(@accent, 35%);
      }
    
      &:active {
        transform: translateY(0);
      }
    }
    Output
    .btn {
      padding: 10px 22px;
      border: 0;
      border-radius: 999px;
      background: #1f6feb;
      color: #fff;
      transition: transform 0.18s ease, box-shadow 0.18s ease;
    }
    .btn:hover {
      transform: translateY(-2px);
      box-shadow: 0 6px 18px rgba(31, 111, 235, 0.35);
    }
    .btn:active {
      transform: translateY(0);
    }

    Switch the styles pane to Less and this is what reaches the preview. The nesting flattens into full selectors, the variables resolve, and fade() becomes the rgba() the browser needs. Worth knowing when a rule does not apply: read the compiled selector rather than the nested one, because the compiled one is what the browser matched against.

  • Wiring the script pane to the markup

    Input
    // JavaScript pane
    const list = document.querySelector('#items');
    
    for (const name of ['alpha', 'beta', 'gamma']) {
      const li = document.createElement('li');
      li.textContent = name;
      list.append(li);
    }
    
    list.addEventListener('click', event => {
      if (event.target.tagName === 'LI') {
        console.log('picked', event.target.textContent);
      }
    });
    Output
    picked beta

    The script runs after the markup has parsed, so querySelector finds the list without waiting on an event. Clicking an item logs to the console panel rather than to your browser console, which is the part people miss: open the panel below the preview, not devtools.

Frequently asked questions

  • How is this different from CodePen or JSFiddle?

    Mostly in what is missing. Those are products with accounts, saved work, collections, embeds, and a following; a pen you save is yours to come back to. This has none of that. There is no login, nothing is stored, and sharing means the code travels in the URL rather than living on a server under an ID. That is worse if you want a library of your own work and better if you want to try something, send it to one person, and never think about it again. It also means nothing you write here is on anybody's server, which matters more for a work reproduction than for a demo.

  • Why is there no SCSS option?

    Because Less already runs on this site and SCSS does not. The Less compiler is a script this page can fetch and call directly, which is what makes the styles toggle a small feature rather than a project. Sass in a browser means the dart-sass build, which is a different size and a different integration, so it is deliberately out of this version rather than planned and missing. If SCSS is what you write, the CSS Prettifier on this site does handle it for formatting.

  • Is my code private?

    It never leaves your browser unless you share it, and sharing puts it in a URL rather than on a server. There is nothing here that stores what you type, no autosave, and no request carrying your panes anywhere. The requests this page does make are for the editor, the Less compiler, and the formatter, all from public CDNs, and none of them carry your code. If you paste a share link into a chat, treat it the way you would treat pasting the code itself, because that is what you are doing.

  • How much code can a share link hold?

    More than is comfortable, and the ceiling is other people's software rather than this page. Browsers handle URLs into the tens of thousands of characters, but a link has to survive whatever it travels through, and the practical limits sit lower: somewhere around two to eight kilobytes before chat clients, ticketing systems, and email clients start wrapping or truncating what they display. A small component comfortably fits. A whole page of markup with a stylesheet attached is where you should expect trouble, and the fix is to trim the reproduction down, which usually makes it a better reproduction anyway.

  • How do I use an external library?

    A script tag in the HTML pane, pointing at a CDN. That runs as part of the document, so a library loaded that way is on the page by the time your script pane executes. A dynamic import from a URL works too, if you would rather write await import("https://esm.sh/canvas-confetti") than a tag. Both need the CDN to send permissive CORS headers. esm.sh, jsDelivr, and unpkg are all set up for that. What does not work is a bare package name, because there is no bundler here to resolve one and no node_modules to resolve it against.

  • Can you build one of these around our own components?

    Yes, and it is a more common request than a general playground. The version people ask for is almost always aimed at their own design system: somewhere a colleague can assemble a component out of the real library, watch it render with the real tokens, and take away markup that will survive contact with the application. The same machinery also makes documentation that runs rather than documentation with a screenshot in it. The editor and the sandbox are the straightforward parts; wiring it to your components and keeping it honest as they change is the work. Zinc Online Solutions does that. Tell us what people should be able to build and who they are.