The full WordPress powered by PHP running solely in the browser with WebAssembly
When you first see WordPress Playground, it seems like an ordinary site–maybe except for the colorful background. It's anything but. What you're actually looking at is an entire WordPress tech stack, including PHP and a database, running directly in your browser.
In this post, Adam Zieliński (lead of WordPress Playground) and Thomas Nattestad (Product Manager for V8) explore:
- How WordPress Playground can help you as a WordPress developer.
- How it works under the hood.
- What it means for the future of WordPress.
Use WordPress without installation, embed it in your app, and even control it with JavaScript
You can use and customize the WordPress embedded at playground.wordpress.net for free. There's no cloud infrastructure and support to pay for, because that site lives entirely in your browser–no one else can visit it. It's also temporary. As soon as you refresh the page, it's gone. You can get as many of these sites as you want for prototyping, trying out plugins, and quickly exploring ideas.
You can even use them to test your code in different environments using the built-in PHP and WordPress version switcher:
WordPress Playground is an entirely new way of using WordPress. Its full power,
however, is only unlocked by including it in your app. The easy way is to embed
WordPress Playground in an <iframe>
and configure it using the
query parameters API.
That's what the official showcase
does. When you select, for example, the
Pendant theme and the
Coblocks plugin, the embedded iframe
gets updated to point to
https://playground.wordpress.net/?theme=pendant&plugin=coblocks.
The iframe is an easy way of getting started, but it's also limited to just the basic configuration option. If you need more than that, there is another, more powerful API.
The WordPress Playground JavaScript client enables full control over the embedded site
You can control the entire WordPress site, including the filesystem and PHP, using the full API available via the @wp-playground/client npm package. The following example shows to use it—check the interactive tutorial for even more examples:
import {
connectPlayground,
login,
connectPlayground,
} from '@wp-playground/client';
const client = await connectPlayground(
document.getElementById('wp'), // An iframe
{ loadRemote: 'https://playground.wordpress.net/remote.html' },
);
await client.isReady();
// Login the user as admin and go to the post editor:
await login(client, 'admin', 'password');
await client.goTo('/wp-admin/post-new.php');
// Run arbitrary PHP code:
await client.run({ code: '<?php echo "Hi!"; ?>' });
// Install a plugin:
const plugin = await fetchZipFile();
await installPlugin(client, plugin);
Use WebAssembly PHP even without WordPress
WordPress Playground is not a monolith. WebAssembly PHP is released independently from WordPress and you can use it separately as well. For the web, you may use the @php-wasm/web npm package optimized for a low bundle size, and in Node.js you can lean on @php-wasm/node which provides more PHP extensions. Adam used the former to add interactive PHP snippets to this WP_HTML_Tag_Processor tutorial. Here's a sneak peek of how to use it:
import { PHP } from '@php-wasm/web';
const php = await PHP.load('8.0', {
requestHandler: {
documentRoot: '/www',
},
});
// Create and run a script directly
php.mkdirTree('/www');
php.writeFile('/www/index.php', `<?php echo "Hello " . $_POST['name']; ?>`);
php.run({ scriptPath: '/www/index.php' });
// Or use the familiar HTTP concepts:
const response = php.request({
method: 'POST',
relativeUrl: '/index.php',
data: { name: 'John' },
});
console.log(response.text); // Hello John
At this point you must be thinking–how does that even work? Great question! Let's dive into the internals and find out. Buckle up!
Under the hood, there's WebAssembly PHP, a SQL translator, and an in-browser server
PHP runs as a WebAssembly binary
PHP doesn't just work in the browser out of the box. WordPress Playground developed a dedicated pipeline to build the PHP interpreter to WebAssembly using Emscripten. Building vanilla PHP isn't overly complex–it only takes adjusting a function signature here, forcing a config variable there, and applying a few small patches. Here's how you can build it yourself:
git clone https://github.com/WordPress/wordpress-playground
cd wordpress-playground && npm install
# Below, you can replace "8.2" with any other valid PHP version number.
npm run recompile:php:web:8.2
However, vanilla PHP builds aren't very useful in the browser. As a server
software, PHP doesn't have a JavaScript API to pass the request body, upload
files, or populate the php://stdin
stream. WordPress Playground had to build
one from scratch. The WebAssembly binary comes with a
dedicated PHP API module
written in C and a
JavaScript PHP class that
exposes methods like writeFile()
or run()
.
Because every PHP version is just a static .wasm
file, the PHP version
switcher is actually pretty boring. It simply tells the browser to download, for
example, php_7_3.wasm
instead of, say, php_8_2.wasm
.
Database is supported with a SQL translation layer
WordPress requires MySQL. However, there isn't a WebAssembly version of MySQL you could run in the browser. WordPress Playground therefore ships PHP with the native SQLite driver and leans on SQLite.
But how can WordPress run on a different database?
Behind the scenes, the official SQLite Database Integration plugin intercepts all MySQL queries and rewrites them in SQLite dialect. The 2.0 release ships a new WordPress Playground-informed translation layer that allows WordPress on SQLite to pass 99% of the WordPress unit test suite.
The web server lives inside the browser
In a regular WordPress, clicking on a link, say Blog, would initiate an HTTP
request to the remote backend to fetch the blog
page. However, WordPress
Playground has no remote backend. It has a
Service Worker
that intercepts all the outgoing requests and passes them to an in-browser PHP
instance running in a separate
Web Worker.
Networking is supported through WebSockets
When it comes to networking, WebAssembly programs are limited to calling JavaScript APIs. It is a safety feature, but also presents a challenge. How do you support low-level, synchronous networking code used by PHP with the high-level asynchronous APIs available in JavaScript?
For WordPress Playground, the answer involves a WebSocket to TCP socket proxy,
Asyncify, and patching deep
PHP internals like php_select
. It's complex, but there's a reward. The
Node.js-targeted PHP build can request web APIs, install composer packages, and
even connect to a MySQL server.
WordPress can be used in even more places than the browser
Since WordPress can now run on WebAssembly, you could also run it in a Node.js server—it's the same V8 engine! Of course with StackBlitz you can also run Node.js directly in the browser, meaning that you could run WordPress and PHP compiled to WebAssembly, executing in Node.js, which is also compiled to WebAssembly running in the browser. WebAssembly is also exploding in popularity in the serverless space, and in the future this could run on that infrastructure as well.
The future may bring zero-setup, interactive, and collaborative WordPress apps
Imagine jumping straight into a code editor where you're free to just get building right away, with all of the setup completed. You could even share a simple link and start a multiplayer editing session, such as in Google Docs. And when you're done, it would only take a single click to seamlessly deploy your creations to a variety of hosting services–all without ever installing anything locally!
And that's just a glimpse! We may see interactive tutorials, live plugin demos, staging sites, decentralized WordPress on edge servers, and even building plugins on your phone.
The future is exciting and you can be a part of it! Your ideas and contributions are the oxygen of WordPress Playground. Visit the GitHub repository, say hi in the #meta-playground WordPress.org Slack channel, and feel free to contact Adam at adam@adamziel.com.