René's Hideout
Builder's guide

Build an HTML puzzle

A self-contained .html file the app can launch, drop a token into, and watch for a completion signal. That's the whole contract.

Quick start

Three steps.

  1. Create a self-contained .html file with your puzzle.
  2. Upload it as an unlock condition in the clue editor.
  3. That's it — the app handles the rest.
The contract

Your HTML file must do two things.

1 Read the token from the URL on page load.

The app passes a secret token via the URL fragment. Read it like this:

const token = new URLSearchParams(
  window.location.hash.substring(1)
).get('token');

2 Signal completion when the player solves it.

Send the token back to the app. Use both methods to support all platforms:

function completePuzzle() {
  if (!token) return;

  // Mobile (Android/iOS)
  if (typeof PuzzleComplete !== 'undefined') {
    PuzzleComplete.postMessage(token);
  }
  // Web
  try {
    if (window.parent !== window)
      window.parent.postMessage(token, '*');
  } catch(e) {}
  try {
    if (window.top !== window)
      window.top.postMessage(token, '*');
  } catch(e) {}
}

Call completePuzzle() when the player wins.

The rules

Four constraints.

One file — inline all CSS and JavaScript. External CDN links (e.g. Google Fonts, libraries) are OK.
No relative paths — the file is served from cloud storage, so relative paths won't resolve.
Don't hardcode the token — it's provided at runtime via the URL fragment.
Mobile-friendly — include <meta name="viewport" content="width=device-width, initial-scale=1.0">
Reference

Minimal working example.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, initial-scale=1.0">
  <title>My Puzzle</title>
</head>
<body>
  <h1>Type the magic word</h1>
  <input type="text" id="answer"
         placeholder="Enter answer...">

  <script>
    const token = new URLSearchParams(
      window.location.hash.substring(1)
    ).get('token');

    function completePuzzle() {
      if (!token) return;
      if (typeof PuzzleComplete !== 'undefined')
        PuzzleComplete.postMessage(token);
      try { if (window.parent !== window)
        window.parent.postMessage(token, '*');
      } catch(e) {}
      try { if (window.top !== window)
        window.top.postMessage(token, '*');
      } catch(e) {}
    }

    document.getElementById('answer')
      .addEventListener('input', (e) => {
        if (e.target.value.trim()
              .toLowerCase() === 'hello') {
          e.target.disabled = true;
          completePuzzle();
        }
      });
  </script>
</body>
</html>
Sparks

Some directions.

You can build anything — the only limit is what HTML, CSS and JS can do:

Word puzzles Code-breaking Jigsaw puzzles Quizzes Logic grids Cipher decoders Hidden objects Memory games 3D mazes Sound-based