Skip to content

Instantly share code, notes, and snippets.

@bgrgicak
Created July 22, 2026 10:42
Show Gist options
  • Select an option

  • Save bgrgicak/a271c24c9d8a9e3eaa229335bac92cb4 to your computer and use it in GitHub Desktop.

Select an option

Save bgrgicak/a271c24c9d8a9e3eaa229335bac92cb4 to your computer and use it in GitHub Desktop.
WP Doom Block Playground blueprint mirror (responsive fix)
{
"$schema": "https://playground.wordpress.net/blueprint-schema.json",
"preferredVersions": {
"php": "8.3",
"wp": "latest"
},
"features": {
"networking": true
},
"landingPage": "/doom-block/",
"meta": {
"title": "Try WP Doom Block in Playground",
"author": "Bero / Gil",
"description": "Installs WP Doom Block and loads a page with the block already inserted."
},
"steps": [
{
"step": "installPlugin",
"pluginData": {
"resource": "literal:directory",
"name": "wp-doom-block",
"files": {
"wp-doom-block.php": "<?php\n/**\n * Plugin Name: WP Doom Block\n * Description: Adds a Gutenberg block that embeds playable Doom in a post or page.\n * Version: 0.1.0\n * Requires at least: 6.5\n * Requires PHP: 8.0\n * Author: Gil / Hermes\n * License: GPL-2.0-or-later\n * License URI: https://www.gnu.org/licenses/gpl-2.0.html\n * Text Domain: wp-doom-block\n *\n * @package WPDoomBlock\n */\n\ndeclare(strict_types=1);\n\nif (! defined('ABSPATH')) {\n exit;\n}\n\nrequire_once __DIR__ . '/includes/render.php';\n\nfunction wp_doom_block_register(): void {\n $dir = __DIR__ . '/build';\n\n register_block_type(\n $dir,\n [\n 'render_callback' => 'wp_doom_block_render',\n ]\n );\n}\nadd_action('init', 'wp_doom_block_register');\n",
"README.md": "# WP Doom Block\n\nA WordPress plugin that adds a Gutenberg block for embedding playable DOOM in a post or page.\n\n## Try it in WordPress Playground\n\n> ## [Launch the WP Doom Block demo in WordPress Playground](https://playground.wordpress.net/?blueprint-url=https%3A%2F%2Fgist.githubusercontent.com%2Fbgrgicak%2F0cdb0637bc50476daed943ed1b0b22a5%2Fraw%2Fdf4a297ae172eed59e44dfd379046afa54f6075d%2Ftry-wp-doom-block-in-playground.json)\n>\n> The source blueprint lives in `blueprints/try-wp-doom-block-in-playground.json` inside this repo.\n>\n> The repo is currently private, so the link above uses a tested public mirror of that blueprint for Playground to fetch.\n\n## Blueprints\n\n- `blueprints/try-wp-doom-block-in-playground.json` \u2014 self-contained demo blueprint for browser Playground links. It installs the plugin and opens a page with the DOOM block already inserted.\n- `blueprints/use-mounted-local-plugin-in-playground-cli.json` \u2014 local development blueprint for Playground CLI. It assumes the plugin is mounted from your working tree, activates it, and seeds the demo page content.\n\n## Development with WordPress Playground CLI\n\nUse the local-development blueprint when you want Playground to run your **mounted local plugin**, not the self-contained browser demo copy.\n\n### Prerequisites\n\n- Node.js 20.18+\n- `npm` / `npx`\n\n### Start a local Playground with the mounted plugin\n\nRun this from the plugin root:\n\n```bash\ncd /path/to/wp-doom-block\nnode -v\nnpx @wp-playground/cli@latest server \\\n --port=9400 \\\n --mount-before-install=\"$(pwd):/wordpress/wp-content/plugins/wp-doom-block\" \\\n --blueprint=./blueprints/use-mounted-local-plugin-in-playground-cli.json\n```\n\nWhy `--mount-before-install` matters:\n\n- the blueprint activates `wp-doom-block/wp-doom-block.php`\n- the plugin must already exist inside `/wordpress/wp-content/plugins/wp-doom-block`\n- mounting before install makes the plugin available when the blueprint runs\n\nOnce Playground boots, it should open `/doom-block/` with the DOOM block already inserted on the page.\n\n### Useful resets\n\nIf you want a fresh Playground state, rerun the same command. If you hit CLI issues, check the latest help output:\n\n```bash\nnpx @wp-playground/cli@latest server --help\nnpx @wp-playground/cli@latest run-blueprint --help\n```\n\n## Inspiration\n\nThis block was inspired by Rhys Wynne\u2019s work on bringing DOOM into WordPress and WordPress Playground.\n\n- Author: Rhys Wynne\n- Blog post: [Can WordPress run Doom? (of course it can, here's how)](https://dwinrhys.com/2024/05/08/can-wordpress-run-doom-of-course-it-can-heres-how/)\n- Related original plugin/repo: [rhyswynne/wpdoom](https://github.com/rhyswynne/wpdoom)\n\nThis plugin is a separate Gutenberg block implementation and does not reuse the original shortcode-based plugin architecture.\n\n## What this plugin does\n\n- Registers a Gutenberg block named `create-block/wp-doom-block`\n- Renders a start button that lazy-loads a DOOM iframe on the front end\n- Lets the editor configure the game URL, frame height, and button text\n\n## Local structure\n\n- `wp-doom-block.php` \u2014 plugin bootstrap and block registration\n- `includes/render.php` \u2014 server-side render helpers\n- `build/` \u2014 block metadata and compiled assets\n- `blueprints/` \u2014 Playground blueprints for browser demos and local CLI development\n- `tests/run.php` \u2014 lightweight verification script\n\n## Verification\n\nRun:\n\n```bash\nphp tests/run.php\n```\n\nExpected output:\n\n```text\nAll tests passed\n```\n",
"includes": {
"render.php": "<?php\n\ndeclare(strict_types=1);\n\nfunction wp_doom_block_escape_url(string $value): string {\n return function_exists('esc_url') ? esc_url($value) : htmlspecialchars($value, ENT_QUOTES, 'UTF-8');\n}\n\nfunction wp_doom_block_escape_attr(string $value): string {\n return function_exists('esc_attr') ? esc_attr($value) : htmlspecialchars($value, ENT_QUOTES, 'UTF-8');\n}\n\nfunction wp_doom_block_escape_html(string $value): string {\n return function_exists('esc_html') ? esc_html($value) : htmlspecialchars($value, ENT_QUOTES, 'UTF-8');\n}\n\nfunction wp_doom_block_render_markup(array $attributes, string $wrapperAttributes = ''): string {\n $defaults = [\n 'gameUrl' => 'https://js-dos.com/games/doom.exe.html',\n 'height' => 600,\n 'startButtonText' => 'Click to play DOOM',\n ];\n\n $attributes = array_merge($defaults, $attributes);\n\n $height = is_numeric($attributes['height']) ? max(240, (int) $attributes['height']) : 600;\n $gameUrl = wp_doom_block_escape_url((string) $attributes['gameUrl']);\n $buttonText = wp_doom_block_escape_html((string) $attributes['startButtonText']);\n $baseWidth = 800;\n\n $wrapperAttributes = trim($wrapperAttributes);\n if ($wrapperAttributes === '') {\n $wrapperAttributes = 'class=\"wp-block-create-block-wp-doom-block wp-doom-block\"';\n }\n\n return sprintf(\n '<div %1$s data-game-url=\"%2$s\">'\n . '<div class=\"wp-doom-block__shell\" style=\"--wp-doom-block-height:%3$dpx\" data-doom-shell>'\n . '<button type=\"button\" class=\"wp-doom-block__start-button\" data-doom-start>%4$s</button>'\n . '<div class=\"wp-doom-block__viewport\" data-doom-viewport hidden>'\n . '<iframe class=\"wp-doom-block__iframe\" title=\"DOOM\" loading=\"lazy\" allowfullscreen data-doom-frame data-src=\"%2$s\" data-base-width=\"%5$d\" data-base-height=\"%3$d\"></iframe>'\n . '</div>'\n . '</div>'\n . '</div>',\n $wrapperAttributes,\n $gameUrl,\n $height,\n $buttonText,\n $baseWidth\n );\n}\n\nfunction wp_doom_block_render(array $attributes, string $content = '', $block = null): string {\n $wrapperAttributes = function_exists('get_block_wrapper_attributes')\n ? get_block_wrapper_attributes([\n 'class' => 'wp-doom-block',\n ])\n : 'class=\"wp-block-create-block-wp-doom-block wp-doom-block\"';\n\n return wp_doom_block_render_markup($attributes, $wrapperAttributes);\n}\n"
},
"build": {
"block.json": "{\n \"$schema\": \"https://schemas.wp.org/trunk/block.json\",\n \"apiVersion\": 3,\n \"name\": \"create-block/wp-doom-block\",\n \"version\": \"0.1.0\",\n \"title\": \"DOOM\",\n \"category\": \"widgets\",\n \"icon\": \"games\",\n \"description\": \"Embed playable DOOM in a post or page.\",\n \"keywords\": [\"doom\", \"game\", \"retro\", \"embed\"],\n \"textdomain\": \"wp-doom-block\",\n \"attributes\": {\n \"gameUrl\": {\n \"type\": \"string\",\n \"default\": \"https://js-dos.com/games/doom.exe.html\"\n },\n \"height\": {\n \"type\": \"number\",\n \"default\": 600\n },\n \"startButtonText\": {\n \"type\": \"string\",\n \"default\": \"Click to play DOOM\"\n }\n },\n \"editorScript\": \"file:./index.js\",\n \"viewScript\": \"file:./view.js\",\n \"style\": \"file:./style.css\"\n}\n",
"index.asset.php": "<?php\nreturn [\n 'dependencies' => [\n 'wp-blocks',\n 'wp-block-editor',\n 'wp-components',\n 'wp-element',\n 'wp-i18n',\n ],\n 'version' => '0.1.0',\n];\n",
"index.js": "( function ( blocks, blockEditor, components, element, i18n ) {\n var el = element.createElement;\n var useBlockProps = blockEditor.useBlockProps;\n var InspectorControls = blockEditor.InspectorControls;\n var PanelBody = components.PanelBody;\n var RangeControl = components.RangeControl;\n var TextControl = components.TextControl;\n var __ = i18n.__;\n\n blocks.registerBlockType( 'create-block/wp-doom-block', {\n edit: function ( props ) {\n var attributes = props.attributes;\n var setAttributes = props.setAttributes;\n var blockProps = useBlockProps( {\n className: 'wp-doom-block-editor',\n } );\n\n return [\n el(\n InspectorControls,\n { key: 'inspector' },\n el(\n PanelBody,\n { title: __( 'DOOM settings', 'wp-doom-block' ), initialOpen: true },\n el( TextControl, {\n label: __( 'Game URL', 'wp-doom-block' ),\n value: attributes.gameUrl || 'https://js-dos.com/games/doom.exe.html',\n onChange: function ( value ) {\n setAttributes( { gameUrl: value } );\n },\n help: __( 'Swap in another js-dos compatible page if you want a different build.', 'wp-doom-block' ),\n } ),\n el( RangeControl, {\n label: __( 'Frame height', 'wp-doom-block' ),\n value: attributes.height || 600,\n onChange: function ( value ) {\n setAttributes( { height: value } );\n },\n min: 240,\n max: 1000,\n } ),\n el( TextControl, {\n label: __( 'Start button text', 'wp-doom-block' ),\n value: attributes.startButtonText || 'Click to play DOOM',\n onChange: function ( value ) {\n setAttributes( { startButtonText: value } );\n },\n } )\n )\n ),\n el(\n 'div',\n Object.assign( { key: 'preview' }, blockProps ),\n el( 'div', { className: 'wp-doom-block__shell', style: { minHeight: ( attributes.height || 600 ) + 'px' } },\n el( 'p', { className: 'wp-doom-block__eyebrow' }, __( 'Playable DOOM block', 'wp-doom-block' ) ),\n el( 'h3', null, 'DOOM' ),\n el( 'p', null, __( 'The front end renders a start button that lazy-loads the game iframe.', 'wp-doom-block' ) ),\n el( 'p', null, __( 'Source:', 'wp-doom-block' ) + ' ' + ( attributes.gameUrl || 'https://js-dos.com/games/doom.exe.html' ) ),\n el( 'button', { type: 'button', className: 'wp-doom-block__start-button', disabled: true }, attributes.startButtonText || 'Click to play DOOM' )\n )\n )\n ];\n },\n save: function () {\n return null;\n },\n } );\n} )( window.wp.blocks, window.wp.blockEditor, window.wp.components, window.wp.element, window.wp.i18n );\n",
"style.css": ".wp-block-create-block-wp-doom-block,\n.wp-doom-block-editor {\n box-sizing: border-box;\n width: 100%;\n max-width: 100%;\n}\n\n.wp-block-create-block-wp-doom-block *,\n.wp-doom-block-editor * {\n box-sizing: border-box;\n}\n\n.wp-doom-block__shell {\n display: flex;\n width: 100%;\n max-width: 100%;\n min-height: var(--wp-doom-block-height, 600px);\n flex-direction: column;\n align-items: stretch;\n justify-content: center;\n gap: 1rem;\n padding: 1.5rem;\n border-radius: 16px;\n overflow: hidden;\n background: radial-gradient(circle at top, #2f2f2f 0%, #111 58%, #000 100%);\n color: #f7f0d0;\n box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);\n}\n\n.wp-doom-block__eyebrow {\n margin: 0;\n font-size: 0.8rem;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: #ffcc66;\n}\n\n.wp-doom-block__start-button {\n align-self: center;\n border: 0;\n border-radius: 999px;\n padding: 0.9rem 1.35rem;\n font-weight: 700;\n cursor: pointer;\n background: linear-gradient(180deg, #ff7b2f 0%, #c93c00 100%);\n color: #fff;\n}\n\n.wp-doom-block__start-button[disabled] {\n cursor: default;\n opacity: 1;\n}\n\n.wp-doom-block__viewport {\n width: 100%;\n max-width: 100%;\n overflow: hidden;\n}\n\n.wp-doom-block__iframe {\n display: block;\n width: 800px;\n max-width: none;\n height: var(--wp-doom-block-height, 600px);\n border: 0;\n border-radius: 12px;\n background: #000;\n transform-origin: top left;\n}\n\n@media (max-width: 782px) {\n .wp-doom-block__shell {\n padding: 1rem;\n }\n}\n",
"view.asset.php": "<?php\nreturn [\n 'dependencies' => [],\n 'version' => '0.1.0',\n];\n",
"view.js": "function wpDoomBlockResize(shell) {\n if (!shell) {\n return;\n }\n\n var viewport = shell.querySelector('[data-doom-viewport]');\n var iframe = shell.querySelector('[data-doom-frame]');\n if (!viewport || !iframe) {\n return;\n }\n\n var baseWidth = parseInt(iframe.dataset.baseWidth || '800', 10);\n var baseHeight = parseInt(iframe.dataset.baseHeight || '600', 10);\n var availableWidth = viewport.clientWidth;\n\n if (!availableWidth || !baseWidth || !baseHeight) {\n return;\n }\n\n var scale = Math.min(1, availableWidth / baseWidth);\n\n iframe.style.width = baseWidth + 'px';\n iframe.style.height = baseHeight + 'px';\n iframe.style.transform = 'scale(' + scale + ')';\n\n viewport.style.height = baseHeight * scale + 'px';\n}\n\nfunction wpDoomBlockBind(shell) {\n if (!shell || shell.dataset.doomBound === 'true') {\n return;\n }\n\n shell.dataset.doomBound = 'true';\n\n var viewport = shell.querySelector('[data-doom-viewport]');\n var iframe = shell.querySelector('[data-doom-frame]');\n if (!viewport || !iframe) {\n return;\n }\n\n if (typeof ResizeObserver !== 'undefined') {\n var observer = new ResizeObserver(function () {\n wpDoomBlockResize(shell);\n });\n observer.observe(shell);\n observer.observe(viewport);\n } else {\n window.addEventListener('resize', function () {\n wpDoomBlockResize(shell);\n });\n }\n\n wpDoomBlockResize(shell);\n}\n\ndocument.querySelectorAll('[data-doom-shell]').forEach(wpDoomBlockBind);\n\ndocument.addEventListener('click', function (event) {\n var button = event.target.closest('[data-doom-start]');\n if (!button) {\n return;\n }\n\n var shell = button.closest('[data-doom-shell]');\n if (!shell) {\n return;\n }\n\n var viewport = shell.querySelector('[data-doom-viewport]');\n var iframe = shell.querySelector('[data-doom-frame]');\n if (!viewport || !iframe) {\n return;\n }\n\n if (!iframe.getAttribute('src')) {\n iframe.setAttribute('src', iframe.dataset.src || 'https://js-dos.com/games/doom.exe.html');\n }\n\n viewport.hidden = false;\n iframe.hidden = false;\n button.hidden = true;\n\n wpDoomBlockResize(shell);\n} );\n"
}
}
},
"options": {
"activate": true
},
"ifAlreadyInstalled": "overwrite"
},
{
"step": "runPHP",
"code": "<?php\nrequire '/wordpress/wp-load.php';\nupdate_option( 'blogname', 'WP Doom Block Demo' );\nupdate_option( 'blogdescription', 'Playable DOOM inside a Gutenberg block' );\nupdate_option( 'permalink_structure', '/%postname%/' );\n$page_id = wp_insert_post( [\n 'post_title' => 'DOOM Block Demo',\n 'post_name' => 'doom-block',\n 'post_status' => 'publish',\n 'post_type' => 'page',\n 'post_content' => '<!-- wp:create-block/wp-doom-block {\\\"height\\\":600,\\\"startButtonText\\\":\\\"Click to play DOOM\\\"} /-->'\n] );\nif ( is_wp_error( $page_id ) ) {\n throw new Exception( $page_id->get_error_message() );\n}\nupdate_option( 'show_on_front', 'page' );\nupdate_option( 'page_on_front', (int) $page_id );\nflush_rewrite_rules();"
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment