Glk is a portable application programming interface (API) for applications, like JACL, with a predominantly text-based user interface. By communicating with this API, JACL is able to work with a variety of user interfaces implemented by third parties for a variety of platforms. The Glk specification was written by Andrew Plotkin, and many thanks to him for having done so. Of equal importance are the various implementations of the Glk specification.
So what does all this mean? The Glk specification defines a set of multimedia functionality that the native console version of JACL does not provide. In the standard Unix distribution, the JACL interpreter is compiled with GlkTerm, a Glk library written by Andrew Plotkin using ncurses. GlkTerm does not support graphics or sound, so the multimedia commands detailed in this chapter are ignored without error by the console interpreter. Multimedia is fully supported by the web interpreters (cgijacl and fcgijacl) which deliver images and sounds through HTML.
If you would like to have your game behave differently if certain Glk features are supported by the interpreter, you can test whether the integer constants graphics_supported, sound_supported and timer_supported are set to true or false.
Graphics, sounds and timers can also be turned off from within your game by setting the integer variables graphics_enabled, sound_enabled and timer_enabled to false.
Some of the text for this chapter has been cheerfully stolen from the Glk specification. The full Glk Specification can be read at http://www.eblong.com/zarf/glk/.
Under Glk, all sound and image files are made available to the interpreter by placing them in a Blorb file with the same base name as the game. For example, the game grail.jacl will look in the file grail.blorb for any sounds or images required.
Under the web interpreters (cgijacl and fcgijacl) the equivalent is a plain-text media manifest with the same base name as the game and the extension .media. The interpreter consults this file to map HTTP requests under the game's URL space to filesystem paths and MIME types. Each non-blank line has three whitespace-separated fields:
<URL path> <MIME type> <filesystem path>
For example, an entry of:
/images/title.png image/png images/title.png
tells the interpreter that a request for /images/title.png should be served as image/png from the relative path images/title.png beneath the projects directory. The same manifest is used for HTML, CSS, JavaScript, fonts, images, and audio — everything the page needs alongside the game text. Because the web interpreter references resources by URL rather than by Blorb index, the image and sound opcodes take a URL string and a MIME type instead of a numeric index (see below).
Blorb is a second specification written by Andrew Plotkin for a common format for storing resources associated with interactive fiction games. There are many tools available for creating Blorb files. A program called bjorb is included in the JACL package that is a slightly modified version of the utility blc. blc is part of the iBlorb suite developed by Ross Raszewski. Many thanks to Ross for both his original development effort and the permission to include this program in the JACL package. Information about iBlorb can be found at http://www.trenchcoatsoft.com/projects.html. The full Blorb Specification can be read at http://www.eblong.com/zarf/blorb/.
The utility bjorb creates a blorb file with the help of a .blc control file. This control file is a plain text file that specifies the sounds and images to include in the blorb file. Each line in a .blc control file describes one chunk of the Blorb file, and has the following format:
Use IndexNumber Type File
Use is the usage of the resource and can either be Pict or Snd.
IndexNumber is the number you will use to refer to this resource in your program.
Type is the resource type. This can be either JPEG, PNG, FORM (Aiff), OGGV or MOD.
File is the name of the file to be included as this resource.
Below is an example .blc file called example.blc:
Pict 1 PNG /images/title.png Snd 3 MOD /music/theme.mod Snd 4 OGGV /sounds/explosion.ogg Pict 2 JPEG /images/car.jpg
To create a Blorb file from this .blc file, use the bjorb utility in the following manner:
bjorb example.blc example.blorb
This command will create the Blorb file example.blorb that will be automatically read by the game example.jacl. When the bjorb utility runs, it will output some JACL code that will create a constant for each image or sound. If you find these convenient you can cut and paste this code into your game.
For example, when reading this .blc file:
Pict 1 PNG images/title.png Pict 2 PNG images/sword.png Pict 3 PNG images/shield.png Pict 4 PNG images/dragon.png
bjorb will produce the following output:
bjorb 1.0 (Apr 30 2008) by Stuart Allen, based on Blorb Packager Version .5b by L. Ross Raszewski # CONSTANTS FOR RESOURCES IN BLORB FILE constant IMAGE_title 1 constant IMAGE_sword 2 constant IMAGE_shield 3 constant IMAGE_dragon 4
If you pass only a single command-line argument to bjorb, that argument will be used as the base name for both the .blc control file and the .blorb output file. As an example, the following two commands are equivalent:
bjorb example bjorb example.blc example.blorb
The image command takes a different form depending on which interpreter is running.
Under Glk, the form is:
image <blorb_index>
The image is drawn at the current point in the text using Glk's inline-down alignment (the top edge of the image aligns with the top of the current line of text). The Glk-side image opcode in earlier versions of JACL accepted an alignment keyword (up, down, centre, left, right) but the current interpreter ignores any alignment argument and always uses inline-down. If you need margin-aligned images, lay them out using stylehint declarations and surrounding text rather than per-image alignment keywords.
Under the web interpreters, the form is:
image "<url>" ["<css_class>"]
The first argument is the URL of the image (a path that must appear in your .media manifest). The optional second argument is a CSS class applied to the rendered <img> element. With no class the interpreter emits <img src="...">; with a class it emits <img class="..." src="...">. Use the class to position or size the image via your stylesheet.
The following code demonstrates the image command:
constant IMAGE_house 4
{+display_image
if interpreter = GLK
# GLK: refer to the image by its index in the blorb file.
image IMAGE_house
else
# WEB: refer to the image by URL (as declared in the .media manifest).
image "/images/house.png" "scene"
endif
}
Like image, the sound command takes a different form on each interpreter.
Under Glk:
sound <blorb_index> [<channel>] [<repeats>]
The sound to play is identified by its index in the game's Blorb file. There are eight available channels numbered 0 through 7 (default 0). repeats is the number of times to play the sound (default 1); -1 loops indefinitely until stop is issued.
Under the web interpreters:
sound "<url>" "<mime>" [<channel>] [loop]
The first argument is the URL of the audio file (which must appear in the .media manifest); the second is its MIME type (for example "audio/ogg" or "audio/mpeg"). The optional third argument is the logical channel. The literal word loop — defined in the standard libraries as a constant with the value -1 — requests indefinite playback. The interpreter emits an <audio> element annotated with data-jacl-channel and data-jacl-pending; the front-end JavaScript replaces any existing audio on the same channel (with a brief cross-fade) when the new sound becomes ready.
The web build also overloads sound for the stop case:
sound 0 <channel>
When the first argument is unquoted, the interpreter treats the call as a stop request and emits a <jacl-sound-stop data-jacl-channel="N"> marker the front-end uses to silence the channel. Under Glk, use the stop opcode instead (see below).
The following code demonstrates the sound command on both interpreters:
constant SOUND_rain 6
constant SOUND_thunder 7
integer AUDIO_CHANNEL 2
{+play_sound
if interpreter = GLK
# GLK: play blorb index 7 on channel 0 once.
sound SOUND_thunder
# GLK: play blorb index 6 on channel 2 indefinitely.
sound SOUND_rain AUDIO_CHANNEL -1
else
# WEB: play /sounds/thunder.ogg on channel 0 once.
sound "/sounds/thunder.ogg" "audio/ogg"
# WEB: play /sounds/rain.ogg on channel 2 indefinitely.
sound "/sounds/rain.ogg" "audio/ogg" 2 loop
endif
}
The volume command is used to set the volume of a sound channel. The volume is specified as an integer between 0 and 100. A second, optional parameter can be used with the volume command specifying which sound channel to set the volume for. If this parameter is omitted the volume is set for channel 0.
The following code demonstrates the volume command:
{+set_volume
# SET CHANNEL 0 TO FULL VOLUME
volume 100
# SET CHANNEL 2 TO HALF VOLUME
volume 50 2
}
Under Glk, the stop command stops the sound being played on the specified sound channel. The channel to stop is specified as an integer between 0 and 7 (matching the eight channels available to sound). If no channel is specified channel 0 is stopped by default.
The following code demonstrates the stop command under Glk:
{+stop_sound
# STOP THE SOUND PLAYING ON CHANNEL 0
stop
# STOP THE SOUND PLAYING ON CHANNEL 2
stop 2
}
Under the web interpreters there is no separate stop opcode; the sound opcode's stop form (sound 0 channel) covers the same case. Code that needs to work under both should branch:
if interpreter = GLK stop 2 else sound 0 2 endif
The timer command tells the interpreter to call the function +timer every so many milliseconds, regardless of whether the player types a command or not. The number of milliseconds to wait between each function call is specified as the timer command's only parameter. If you specify a time of 0 to a timer command, the timer will be turned off.
![]() |
It is important to be aware that not all interpreters will support the timer command, so no processing essential to the game should be performed within the +timer function. Periodically playing sound effects that are not essential to the game is an example of valid use of this functionality. |
Below is an example of the timer command being used:
{+intro
...
# SET THE TIMER TO EVERY TEN SECONDS
timer 10000
...
}
{+timer
if here has OUTDOORS
# PLAY THE THUNDER SOUND ON CHANNEL 3 SO
# IT DOESN'T INTERFERE WITH OTHER SOUNDS
sound SOUND_thunder 3
endif
}
The style command is used to change the appearance of subsequent text output. Depending on the interpreter being used, it sets a Glk stream style, emits ANSI terminal codes, or writes an HTML tag. The style command accepts a single string containing the name of the style to set. Here is an example of using the style command to output some bold text:
style bold write "This is bold.^" style normal
Below is a table showing the styles available in JACL and how each one is rendered by the Glk and web (cgijacl / fcgijacl) interpreters:
| JACL Style | Glk Style | Web (HTML) |
|---|---|---|
| bold (or emphasised) | style_Emphasized | <b> |
| note | style_Note | <i> |
| input | style_Input | <i> |
| header | style_Header | <h1> |
| subheader | style_Subheader | <h2> |
| reverse (or inverse) | style_User1 / style_User2 | <b> |
| pre (or preformatted) | style_Preformatted | <pre> |
| alert | style_Alert | <strong> |
| quote (or blockquote) | style_BlockQuote | <blockquote> |
| normal | style_Normal | closes all open style tags |
The alert and quote styles are extra colourable buckets exposed by JACL and have no built-in default beyond what the interpreter chooses; pair them with stylehint declarations (see below) to give them a colour appropriate to your game.
A few things to keep in mind when targeting the web interpreters:
The default appearance of each style is up to the interpreter and the user's preferences. To recolour a style for your game, use the top-level stylehint declaration. Like constant, stylehint sits outside any function block and is processed once when the game file is loaded -- before any windows are opened. This is the only place colour can be set, because the Glk specification only honours style hints that are recorded before window creation.
stylehint header textcolor "white" stylehint header backcolor "darkblue" stylehint note textcolor "#00ccff" stylehint reverse textcolor "white" stylehint reverse backcolor "red"
The general form is:
stylehint <style> <attribute> <value>
Where <style> is one of the JACL style names from the table above (bold, note, input, header, subheader, reverse, pre, alert, or quote; the synonyms emphasised, inverse, preformatted, and blockquote are also accepted), <attribute> is one of:
| Attribute | Aliases | Meaning |
|---|---|---|
| textcolor | color, colour | Foreground (text) colour for the style. |
| backcolor | background, bgcolor | Background colour for the style. |
| reverse | reversecolor | An integer flag (0 or 1) requesting the interpreter to swap foreground and background for the style. Glk-only; ignored on the web. |
And <value> for the colour attributes is either a named colour or a quoted hex string of the form "#rrggbb" or "#rgb". The recognised colour names are:
black, white, red, green, blue, yellow, cyan, magenta, gray (or grey), lightgray (lightgrey), darkgray (darkgrey), orange, purple, brown, and pink.
Behaviour by interpreter:
The reverse JACL style is special: under Glk it sets style hints on the user-defined styles style_User2 (main window) and style_User1 (status window), since those are the slots the runtime style reverse command targets.
![]() |
The same status-window code works unchanged under the web interpreters (cgijacl and fcgijacl). The runtime intercepts the cursor and write opcodes used inside +update_status_window, builds an in-memory grid the same shape as the Glk text grid would have, and emits it as an HTML <table> with one cell per character position. You write your status code once; Glk renders it as a fixed-width window, the web emits it as a styled HTML grid. |
The status window at the top of the screen is implemented as a Glk window, and is therefore covered here. It is possible to have no status window at all, use the built-in, default status window or design a custom one yourself. The vertical height of the status window in rows is defined by using the constant status_window. If the constant status_window is set to 0, no status window will be created. For example, to create a status window that is three rows tall, define the following constant:
constant status_window 3
The status window is always created using a fixed-width font and its current dimensions can be read at any time using the values of status_width and status_height. Although status_height will most often be equal to status_window (unless status_window exceeded the available space), status_width will change as the player resizes the game window.
![]() |
Under the web interpreter, the rendered bar height is clamped at 10 rows regardless of status_window, so a very tall value will not eat the whole viewport. status_width is set per request from the browser's measured status_cols; do not set status_window_width from game source. |
If a status window is created, the interpreter will attempt to call the global function +update_status_window. If this function exists, it will be executed and must contain code to draw the contents of the status window. If this function doesn't exist, the interpreter will use internal code to generate a standard interactive fiction status line. This consists of a single line with the name of the current location against the left side and the number of moves and current score against the right.
If the function +update_status_window does exist, the current Glk stream is set to that window before it is called so all write or print commands will output to it. The window is also first cleared of all previous contents and the cursor is positioned in the top left corner.
The location at which to start printing text within the status window is changed using the cursor command. The cursor command is passed two integer parameters, the column and row to move the cursor to. Counting starts in the top left corner at 0, 0. For example, to move the cursor to the far right hand column of the second row, use the command:
cursor status_width 1
Often when positioning text it is important to know the length of the string you are going to print. This is determined using the length command. The length command requires two parameters: the container to hold the length of the string and the string itself. For example, the following command determines the length of the string constant game_title and stores the result in the variable index:
length index game_title
Putting this all together, it is possible to display the title of the game centred in a single-line status window using the following code:
constant game_title "The Unholy Grail"
constant status_window 1
integer index
integer offset
{+update_status_window
set offset = status_width
length index game_title
set offset - index
set offset / 2
cursor offset 0
write game_title
}
Status windows are often displayed using reverse text to make it clearly stand out from the main window. This is achieved using the following command:
style reverse
This command, however, will only reverse the text output, not the whole window. In order to achieve the effect of an entirely reversed window, blank spaces will need to be printed wherever there isn't any other text. The easiest way to do this is to print entire rows of blank spaces then move the cursor back to print over the top. The padstring command exists to help with this process. The padstring command takes three parameters. The label of the string to fill, the text to fill the string with and an integer specifying the number of times to copy the text into the string. To print a blank line in the status window, the text that will by copied is a single space in quotes (" ") and it will be copied status_width times. The code below is an expanded version of the above function that prints the title of the game centred in an inverse status window:
constant game_title "The Lovely Test Game"
string status_text
integer index
integer offset
constant status_window 1
{+update_status_window
style reverse
padstring status_text " " status_width
write status_text
set offset = status_width
length index game_title
set offset - index
set offset / 2
cursor offset 0
write game_title
}
As a final example of a +update_status_window function, below is the JACL code to replicate the internal status line produced if no custom function is provided:
string status_text
integer index
constant status_window 1
{+update_status_window
style reverse
padstring status_text " " status_width
write status_text
cursor 1 0
write here{The}
setstring status_text "Score: " score " Moves: " total_moves
set offset = status_width
length index status_text
set offset - index
set offset - 1
cursor offset 0
write status_text
}
The interpreter will call the +update_status_window function after each of the player's moves and when the game window is resized. If you require the status window to be updated at other times such as in a loop or from the +timer function, use the updatestatus command. The updatestatus command takes no parameters. It sets the current output stream to the status window and clears the status window before calling the function +update_status_window. When +update_status_window has finished executing the current output stream is set back to the main window.
![]() |
It is not possible to call the +update_status_window directly. It will be called by the interpreter automatically after each of the player's moves or when the window is resized. If you do require the window to be updated at other times use the updatestatus command. If you call +update_status_window directly the current output stream will not be set to the correct window. |