Skip to content

How to webify a puzzle

Kenny Young edited this page Feb 6, 2025 · 46 revisions

Back to the Webification hub

Do you normally create your puzzles in Publisher and deliver as PDF? Are you scared about the idea of turning your static puzzle into an interactive webpage? Don't worry, it's a lot easier than you think!

If you have never written HTML and are interested in an overview, you can optionally take a look at My First HTML Course - but if your puzzle is very simple (e.g. some intro text and a grid), you will need none of it.

Basic webification steps

  1. Mark yourself on the Sharepoint as a webifier for the puzzle (if your name isn't in the list, just use the !!self option)
  2. Copy the example puzzle folder in the puzzles folder and rename the copied folder for your puzzle (use your puzzle's full name, omit non-alphanumeric characters, and see Style Guide below) while leaving the index.html file inside as index.html
  3. Search index.html for CHANGE_ME comments and make all the changes they call out.
  4. Everywhere possible, use PuzzleJS, which already does a large amount of the heavy lifting. The example puzzle is prewired to use PuzzleJS, and contains everything you need to get started. When using PuzzleJS, most puzzles can be webified with no additional JavaScript, and you can add as much CSS as you want to get the look you have in mind.
  5. Put any extra files your puzzle needs in your puzzle folder; subfolders are OK. Do not add or modify any files in the resources folder without talking to Kenny first.
  6. Make a branch in Git (if you don't have one already) and commit your changes. After committing to your branch, create a pull request for main, and add tabascq (Kenny) as a reviewer. You can put multiple puzzles in the same pull request if desired. If this item is pure gibberish to you, visit Working with our webification repo for more details!
  7. Make a zip file of your puzzle's folder (the one you created in step 2).
  8. Log in to the alpha site, and switch to author mode by clicking on the words "Puzzleday Alpha" in the top bar on the page. If you aren't offered a popup including a switch to Author mode, talk to Kenny and he will set you up.
  9. If you haven't created this puzzle yet, click on "Puzzles" on the top bar, and then click "Create new puzzle" on that page. Fill in the title and press Create.
  10. Click the Files button near the top of your puzzle's Properties page. Upload the zip to the Materials section of your puzzle. If you're replacing any files, delete the existing copies of those files first. Click on the link for index.html and make sure that it works.
  11. Go to the Responses page for your puzzle, and add one or more responses for correct or partial submissions. One of them should have IsSolution checked and have a response of "Correct!". Some events require one and only one solution, though Puzzleday usually permits multiple.
  12. Go back to the Details page for your puzzle, set AlphaTestsNeeded (in the top right of the properties list) to equal the number of tests you want (usually 2), and click Save Properties. Your puzzle is ready to test!
  13. When you're ready to write your solution, do steps 2-10 again, except start with the example solution and put it in the solutions directory in a folder named <name of your puzzle folder>-solution, and upload it to the "Solution Materials and Tokens" section of the Files page of your puzzle (NOT Materials).

PuzzleJS

PuzzleJS is a JavaScript library primarily written and maintained by Kenny/tabascq that allows for very easy creation of interactive puzzle grids and more. See that link for large numbers of examples.

The example puzzle shows how to use PuzzleJS, and also shows how to use the built-in designer. For simple puzzles, this is all you need to know! Note that every property header in the designer is a link to its documentation, and some properties have recorders that let you just draw the desired visuals in the puzzle grid and then snapshot the result.

PuzzleJS does a lot of the heavy lifting for you: it has built-in undo/redo support, saves its state when the user leaves the page, offers a lot of accessibility, etc. You won't have to write any JavaScript unless you are doing something really advanced, and if you want to change how the puzzle looks you can do that with some basic CSS (see the Styling Reference).

PuzzleJS Designer

The designer can be accessed by pressing Shift + Ctrl + Alt + F12 on one of our puzzle pages, and can be used to edit any puzzle grid.

Once you have selected a puzzle, edit the properties of your puzzle using the categories and properties on the right. The name of each property is a hyperlink to the documentation for that property.

Several properties have a record button, denoted by ⭕. To work with these properties, toggle record mode on to 🔴, draw or type directly within the puzzle, and then toggle record mode back off to ⭕. The property will update when recording stops.

In recording mode for the data-text property, the . (editable blank cell), # (editable extract cell - see here), and @ (uneditable black cell) characters display literally, to facilitate editing. Also, data-text-replacements is turned off for the same reason.

When you're done editing a puzzle in the PuzzleJS designer, scroll down to the Import/Export category, copy the HTML you'll find there, and paste it into your source code at the appropriate spot (where the puzzle you were editing was found).

Helpful intermediate topics

Extracting text from cells into a final answer

One puzzle that does this is Scene It!, which is located in the pd2025-webification repository in the old-event-libraries/pd2023-library/puzzles/scene-it folder and can be played from there. This puzzle uses the text handling capabilities of PuzzleJS to automatically extract letters from certain boxes and put them down in a dedicated answer space:
image

Here's what the code to do that looks like:

<html lang="en-us">
...
<body>
<div class="page-div">
    ...
    <div class="content-div online-only">
        <ol>
            <li>
                <video controls height="360">
                    <source src="resources/01.mp4" type="video/mp4">
                    Your browser does not support the HTML video tag.
                </video>
                <div class="puzzle-entry" data-mode="linear" data-text=".#..... #. ... ......#.#" data-extracts="9 28 4 1"></div>
            </li>
            ...
            <li>
                <video controls height="360">
                    <source src="resources/11.mp4" type="video/mp4">
                    Your browser does not support the HTML video tag.
                </video>
                <div class="puzzle-entry" data-mode="linear" data-text="# ... .#....#." data-extracts="25 12 33"></div>
            </li>
        </ol>

        <div class="puzzle-entry" data-mode="linear" data-text="##################" data-extracts="1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18"></div>
        <div class="puzzle-entry" data-mode="linear" data-text="##################" data-extracts="19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36"></div>
    </div> <!-- content-div -->
    <div class="content-div print-only">
        This puzzle contains videos that are only available online.
    </div>
</div>
</body>
</html>

Structurally, you can see that each video-text pair is actually an HTML list, although there's no bullet point because it's been taken off via CSS. The video files are included in the repository alongside the puzzle in a resources folder; if you wanted to use Youtube videos instead, you'd need an iframe instead of a video tag. You can also see that each text input is its own puzzle-entry div, as is the answer extraction! Finally, you can see that there are actually two content-div sections, one marked print-only, and one marked online-only. CSS supports styling printed pages, so all those classes are doing is setting the display property to "none" when this puzzle isn't being viewed using the appropriate medium. That way, a user doesn't try to print videos!

To define the puzzles, you see that the text attribute uses a combination of #,., and spaces, where a . is a blank box and a # is a yellow box whose letter will automatically be copied over to a corresponding answer box. The specific answer box it's tied to is defined by a numbered index, as seen in the extracts attribute. In fact, there's nothing special about the answer extraction lines; since they don't have user input disabled on them, a user could actually type into them and have it appear under the corresponding puzzle!

Clone this wiki locally