Back to Blog
WordPress Playground: When Your CMS Needs to Live Entirely in the Browser
10 min readJul 20, 20263 views

WordPress Playground: When Your CMS Needs to Live Entirely in the Browser

WordPress in the browser? With WebAssembly PHP, it's not just a demo, it's a game-changer for development, demos, and even local content editing without a server setup.

Web DevelopmentWebAssemblyToolingFull-Stack
Share

by Sunil Band

Your Full-Stack App, Running Entirely in the Browser

We've all been there: you need a quick WordPress site for a demo, a theme test, or maybe even to prototype some content. What's the process? Spin up Docker, configure a local server, wrangle a database, and then finally install WordPress. It's a significant overhead for something that often feels like it should be simpler. This friction often makes us reach for a static site generator or a headless CMS, even when WordPress's content-editing capabilities are exactly what we need.

But what if you could have the entire WordPress stack – PHP, MySQL, and all – running directly in your browser? No Docker, no local server, no database setup. Just a single JavaScript file. This isn't science fiction; it's WordPress Playground, powered by WebAssembly PHP and a SQLite database. It's a paradigm shift for how we think about full-stack web applications, moving server-side logic and persistence straight into the client.

For years, the browser was a client-side rendering machine, fetching data and HTML from a server. Then came Node.js, blurring the lines by letting JavaScript run on the server. Now, with WebAssembly, we're seeing the pendulum swing again, allowing traditionally server-side languages like PHP and even databases to execute client-side. This isn't about replacing your production infrastructure, but about enabling entirely new workflows and development paradigms that were previously impossible or impractical.

The Magic Behind the Curtain: WebAssembly PHP

The core innovation enabling WordPress Playground is WebAssembly PHP. It's exactly what it sounds like: the PHP interpreter compiled to WebAssembly. This means the actual PHP code that powers WordPress, your plugins, and your themes can run directly in the browser's JavaScript runtime, in a sandboxed environment.

Pairing this with a SQLite database, also compiled to WebAssembly and running in the browser, means the entire traditional LAMP stack (minus Apache/Nginx, as the browser handles HTTP requests) is now client-side. The browser becomes its own self-contained server environment. The implications are huge: instant WordPress instances, offline development, shareable demos as simple URLs, and even highly dynamic content editing that doesn't require a network roundtrip.

How It Works: A Simplified View

When you load a WordPress Playground instance, a few things happen under the hood:

  1. WebAssembly PHP Module: The PHP interpreter, compiled to .wasm format, is downloaded and initialized by the browser. This module exposes functions to run PHP code.
  2. Virtual File System: A JavaScript-based virtual file system (often backed by something like IndexedDB for persistence) is created in the browser. This is where all WordPress files, themes, plugins, and user uploads reside.
  3. SQLite Database: A SQLite database, also a WebAssembly module, is loaded. It stores all WordPress data – posts, users, settings, etc. – within the browser's local storage or memory.
  4. Request Handling: Instead of sending HTTP requests to a remote server, the Playground intercepts requests within the browser. These requests are then routed to the WebAssembly PHP interpreter, which processes them using the virtual file system and SQLite database, generating HTML that the browser then renders.

It's a complete encapsulation. Your browser isn't just displaying a webpage; it's hosting a dynamic, full-stack application. It feels like magic, but it's really just brilliant engineering leveraging modern browser capabilities.

Building a "Serverless" WordPress Demo

Let's walk through how you might integrate WordPress Playground into a simple web application to create an ephemeral, in-browser WordPress instance. We'll use the @php-wasm/web package, which is the underlying library powering WebAssembly PHP.

First, you'll need to install the package:

bash
npm install @php-wasm/web

Now, let's set up a basic HTML page and some JavaScript to load WordPress. The key is understanding that you need to provide WordPress's files to the virtual file system.

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>In-Browser WordPress Playground</title>
    <style>
        body, html { margin: 0; padding: 0; height: 100%; overflow: hidden; }
        #wordpress-iframe { width: 100%; height: 100%; border: none; }
        #loading-overlay { 
            position: absolute; top: 0; left: 0; width: 100%; height: 100%;
            background: rgba(0,0,0,0.8); color: white; display: flex;
            justify-content: center; align-items: center; font-family: sans-serif;
            font-size: 2em; z-index: 1000;
        }
    </style>
</head>
<body>
    <div id="loading-overlay">Loading WordPress...</div>
    <iframe id="wordpress-iframe"></iframe>

    <script type="module">
        import { PHP } from '@php-wasm/web';

        const loadingOverlay = document.getElementById('loading-overlay');
        const wordpressIframe = document.getElementById('wordpress-iframe');

        async function initializeWordPress() {
            try {
                // Instantiate PHP-WASM. This loads the PHP interpreter.
                const php = await PHP.load({
                    // Path to the directory containing PHP-WASM assets (php.wasm, php.ini, etc.)
                    // Make sure these are served correctly by your web server.
                    wasmUrl: '/php-wasm-assets/php.wasm',
                    phpIni: [
                        'display_errors=1',
                        'error_reporting=E_ALL',
                        'log_errors=0'
                    ].join('\n')
                });

                // You'll need to fetch the WordPress zip and extract its contents
                // into the PHP-WASM's virtual file system.
                // For a real app, you'd fetch the latest WordPress.zip.
                // For this example, let's assume 'wordpress-files' is a directory
                // served statically by your dev server containing WordPress.
                console.log('Fetching WordPress files...');
                
                // In a real scenario, you'd download wordpress.zip and extract it
                // into php.mount('/wordpress', ...). For simplicity, we'll simulate
                // having files available. WordPress Playground's actual implementation
                // handles this robustly.

                // Simulate mounting WordPress files (you'd copy actual files here)
                // For a working example, you need a pre-downloaded 'wordpress' directory
                // at the root of your web server.
                const wordpressDir = await fetch('/wordpress-mock-files/wp-config.php'); // Just fetch one file to confirm availability
                if (!wordpressDir.ok) {
                    throw new Error("WordPress files not found. Make sure '/wordpress-mock-files' is accessible.");
                }

                // This is where you would traditionally `mount` a directory
                // php.mount('/wordpress', { source: 'wordpress-mock-files' }); 
                // However, directly mounting from a URL is complex due to browser security.
                // WordPress Playground abstracts this by having a pre-packaged Wasm build.

                // For a true in-browser WordPress experience, you'd use the WordPress Playground API directly.
                // This example demonstrates the underlying PHP-WASM capability.
                // The actual WordPress Playground uses an iframe with a specific URL structure
                // to load WordPress from their CDN or a local source.
                
                // Let's create a minimal `index.php` in the virtual file system to show PHP working
                php.writeFile(
                    '/index.php',
                    `<?php echo '<h1>Hello from PHP-WASM!</h1>'; phpinfo(); ?>`
                );

                // Simulate an HTTP request to our in-browser PHP. This is where
                // the magic happens, routing browser requests to the WASM PHP.
                const response = await php.request({
                    method: 'GET',
                    url: '/index.php',
                    _GET: { hello: 'world' }
                });

                const output = response.text;

                // Display the output in the iframe
                const iframeDoc = wordpressIframe.contentDocument || wordpressIframe.contentWindow.document;
                iframeDoc.open();
                iframeDoc.write(output);
                iframeDoc.close();

                loadingOverlay.style.display = 'none';

            } catch (error) {
                console.error("Failed to load WordPress in browser:", error);
                loadingOverlay.textContent = `Error: ${error.message}`;
            }
        }

        initializeWordPress();
    </script>
</body>
</html>

A word of caution: The example above demonstrates the raw PHP.load() and php.request() from @php-wasm/web. To get a full WordPress instance, you actually need to supply all WordPress core files, plugins, and themes to its virtual file system. The official WordPress Playground project at wordpress.github.io/wordpress-playground handles this by providing pre-packaged WebAssembly builds of WordPress, or by dynamically fetching and extracting a WordPress zip into the virtual file system. My code snippet is more illustrative of the underlying tech than a production-ready Playground setup.

Typically, you'd embed the Playground using their provided client library which simplifies this immensely:

typescript
import { createPlayground } from '@wordpress/playground';

async function loadPlayground() {
  const playground = await createPlayground();
  // Now 'playground' is an iframe containing a running WordPress instance
  document.body.appendChild(playground.iframe);
  // You can even connect to it and run WP-CLI commands:
  // await playground.run({
  //   command: ['wp', 'plugin', 'install', 'gutenberg', '--activate']
  // });
}

loadPlayground();

This is much simpler and shows the true power of the Playground API: you get a full WordPress instance with a single function call, and you can programmatically interact with it. This is how the official Playground website generates custom WordPress instances with specific plugins or themes loaded, all from a URL query parameter.

Use Cases Beyond Demos

WordPress Playground isn't just a novelty; it unlocks several powerful use cases:

  • Instant Development Environments: Quickly spin up a fresh WordPress instance for theme or plugin development without touching your local server stack. This is particularly useful for contributors or trying out new ideas.
  • Interactive Documentation/Tutorials: Embed live, editable WordPress instances directly into blog posts or documentation, allowing users to try things out without leaving the page. Imagine a "Try it now" button that gives you a fully functional WordPress admin.
  • Offline Content Editing: For specific use cases, a browser-based WordPress could allow content creators to work offline, syncing changes when connectivity returns. This relies on the IndexedDB persistence capabilities.
  • Automated Testing: Programmatically control a WordPress instance in a headless browser for integration and E2E testing of plugins and themes, providing a consistent and isolated environment every time.
  • Educational Tools: Teach WordPress development or content management in an entirely browser-based environment, simplifying setup for students.

This shift profoundly changes the developer experience for WordPress. The barrier to entry drops significantly, making it easier to experiment, share, and collaborate.

The Trade-offs and Realities

While incredibly powerful, running a full PHP application in the browser comes with some practical considerations:

  • Performance: WebAssembly PHP is fast, but it's still running within a JavaScript engine. Computationally intensive tasks will naturally be slower than on a native server. Also, the initial load time can be significant as the .wasm files and WordPress core are downloaded.
  • Resource Usage: Running a PHP interpreter and a database in the browser consumes client-side memory and CPU. This needs to be managed, especially on less powerful devices.
  • Persistence: By default, browser storage (like IndexedDB) has limits and can be cleared by the user or browser. For long-term, critical data, a true server-side database is still the way to go. Playground often uses session storage for ephemeral instances.
  • Network Access: A browser-based WordPress cannot make direct outbound network requests to other servers (e.g., to fetch external APIs) in the same way a server-side PHP application can, due to browser security models. All such requests would need to be proxied or handled by JavaScript.
  • File Uploads/Server-side Operations: Operations like image processing that typically rely on server-side libraries (GD, ImageMagick) might be limited or require WebAssembly versions of those libraries, which adds complexity.

WordPress Playground is not a replacement for your production WordPress server. Its strength lies in its ability to create isolated, disposable, and shareable WordPress instances for specific, client-side-centric workflows. It's a developer tool, an educational platform, and a demo environment, not a scalable production host.

Wrapping up

WordPress Playground is more than just a cool tech demo; it's a testament to the power of WebAssembly and a glimpse into a future where traditional server-side applications can be seamlessly integrated into the browser. It drastically lowers the barrier to entry for WordPress development and experimentation, allowing you to spin up a full environment with a single click.

My advice? Head over to the official WordPress Playground website and play around. Try generating a custom URL with specific plugins and themes, and consider how you might embed this instant WordPress experience into your own documentation, tutorials, or even internal tools. The @wordpress/playground NPM package is where you'll find the client-side API to build custom experiences, and it's a fantastic starting point for pushing the boundaries of what's possible directly in the browser.

More from the blog
Available for projectsReady to make something fun 🎈

Ready to build the next system?Wanna build something awesome together?

Currently accepting high-impact opportunities in frontend engineering and scalable web applications.Got a cool idea rattling around? Let's grab a virtual coffee and turn it into something people love. ☕