GUI Web Interface

The library file webinterface.library ships a complete web frontend for cgijacl-served games. The earlier three-mode swap (Ajax / Standard / GUI) has been retired in favour of a single hyperlinked layout: a maintext window above a status bar, with a directions strip and command prompt in the footer. Several library hooks and <jacl-*> DOM markers let the interpreter drive incremental updates without forcing a full page reload.

Below is a screen shot of the web interface in use:

 
The JACL web interface
 
If you want your game to be playable using the link bar and command form alone, design each puzzle so it can be reached via the verbs the interface exposes — the link bar is a supplement to typed input, not a replacement for it.

Using the Interface

To use this web interface include the library at the bottom of your game file:

#include "webinterface.library"
#include "webinterface.css"

Once included, define the appearance constants you want to override. Here are the values from The Down Dragon, as shown in the screenshot above:

constant title_image       "/images/dragon.png"
constant footer_image      "none"
constant header_colour     "#42596d"
constant linkbar_colour    "#3f3527"
constant maintext_colour   "#dddddd"
constant title_colour      "black"

Colours can use any value the CSS specification accepts; hexadecimal RGB gives the finest control over shade.

For per-scene background pictures, set the string current_image to a URL at any time during play. The footer's picture pane will display whatever current_image currently points to (use "none" to hide it).

The Link Bar

The library's +header function builds a left-to-right strip of buttons using the hyperlink opcode:

hyperlink "Instructions" "instructions" "header"
hyperlink "Map"          "map window"   "header"
hyperlink "About"        "about"        "header"
hyperlink "Restart"      "restart"      "header"
hyperlink "Hint"         "hint"         "header"

hyperlink label command class writes an anchor element that, when clicked, submits command through the game's normal parser. class is the CSS class applied to the link (the bundled stylesheet defines a header class for link-bar buttons, but you can use any class you like). You can call hyperlink from any function, not just +header, to embed clickable verbs anywhere in the output. Under Glk the opcode degrades gracefully — the label is written as plain text and the command argument is ignored.

The Google sign-in button is appended to the link bar automatically when google_client_id is configured (see the HTTP chapter for the full Google Sign-In setup).

DOM Contract

The page emitted by +header and +footer exposes a fixed set of element IDs that the interpreter and ajax response handler rely on. If you customise the layout, keep these landmarks intact:
ElementPurpose
#mainThe scroll container.
#maintextThe game's running text output. The interpreter writes here.
#statuswinThe status bar (above maintext).
#footer .directionsThe exits list, refreshed after every turn.
#commandFormThe form used to submit player input.
#JACLCommandPromptThe text input emitted by the prompt opcode.

The interpreter emits a small set of marker elements that the front-end interprets out-of-band. They never appear in the visible text — the JavaScript handler strips them after acting on them.
MarkerEmitted byEffect on the page
<jacl-clear>the clear opcodeWipe everything in #maintext above this marker.
<jacl-timer data-ms="N">the timer N opcodeBegin firing ?rpc=timer ajax calls every N milliseconds. timer 0 cancels.
<jacl-status data-lines="N">status writesReplace the contents of #statuswin with the marker's inner HTML.
<jacl-directions>the library's +library_eachturnReplace the exits strip in the footer.
<audio data-jacl-channel="N" data-jacl-pending="1">the sound opcodeStart playing on logical channel N.
<jacl-sound-stop data-jacl-channel="N">sound 0 NStop channel N.
<jacl-sound-volume data-jacl-channel data-jacl-level>volume changesSet the gain on channel N.

After every turn-consuming command, verbs.library's +system_eachturn calls into +library_eachturn, which wraps the current exits in a <jacl-directions> marker. The ajax handler then swaps the marker's contents into the footer's directions strip so the bar stays current after movements, door opens and the like.

Status-Bar Resize Round-Trip

The status bar is rendered into a virtual character grid sized by status_window_width. On the web, that width is set by the JavaScript front-end — on page load and on every browser-window resize event — via a status_cols URL parameter sent with each ajax request. When only the window has resized (no command typed), the library fires a debounced ?ajax=true&rpc=resize&status_cols=N request that asks the engine to re-render the bar at the new width. The response carries a fresh <jacl-status> marker which the page applies to #statuswin. Stale resize responses (one arrived after a newer one or after a real command response) are detected via a monotonic token and discarded.

In-flight Command Guard

The library sets window.jaclSubmitInflight = true when the player presses Enter and clears it when the server response arrives (success or error). The command input is disabled during the round-trip. Custom JavaScript that pokes #JACLCommandPrompt directly should check the flag before assuming the field is editable, and should respect it as a hint that another submit is pending.

Pending-Question Prompts

The web build supports two opcodes for asking the player a one-off question without consuming a turn:

getyesorno var
getnumber var low high

When either runs, the interpreter records the question type in pending_question_type (and, for getnumber, the bounds in pending_number_low and pending_number_high) and the page enters a special input state in which the next command is parsed as the answer rather than as a verb. The library's +game_intro scaffold relies on this to ask the voice and sound preferences before the opening prose runs — your +intro re-enters after each answer until both questions have been resolved.

You can opt out of the voice / sound questions for utility games (life, blackjacl, etc.) by setting skip_intro_preferences to true in your +intro before calling +game_intro.

Voice (Text-to-Speech)

Browser SpeechSynthesis is used to read game output aloud. The player can enable it with voice, disable it with silence or mute, list the available voices with voices, and pick a specific one with voice n. The library tracks the current state in voice_enabled; the +startup_preferences hook in +game_intro asks the player up front whether they want it on.

Authorial Scaffolding

+game_intro in webinterface.library provides a standard intro scaffold for web games. Provide two functions in your game:

Your +intro shrinks to whatever per-entry state your game needs plus execute "+game_intro":

{+intro
set player = kryten
execute "+game_intro"
}

The web build re-enters +intro after every pending-question answer (TOTAL_MOVES is still 0 throughout this window), so the scaffold uses the guards intro_dedication_shown and intro_scene_shown to fire each block exactly once. Under Glk the scaffold runs straight through synchronously because getyesorno blocks for input.

By convention each game's +game_scene starts with clear so the opening prose appears at the top of a fresh window once the dedication and Q&A have been answered. Doing the clear inside +game_scene keeps it gated by the same flag that protects the scene block.

Back to Contents