Skip to content

Accessibility

Kit Oliynyk edited this page Nov 2, 2026 · 1 revision

TL;DR: Blind players use the game with screen readers, so no fact may live only in color, an image or an ASCII drawing. A single switch, config screenreader, tells the engine and scripts to print words instead of decoration. Every visual you add needs a linear text twin. Use when: you add a table, a map, a bar, a color-coded marker, a frame or any layout that a speech engine would read as noise. Source of truth: dreamland_code/plug-ins/output/screenreader.cpp (uses_screenreader), dreamland_code/plug-ins/output/mudtags.cpp (visibility tags), dreamland_world/helps/help.xml (help 2132), mudjs/src/components/terminal.jsx Verified: 2026-11-02 against dreamland_code 88da8302, dreamland_world 43880467, mudjs 990bf6d

About 30% of active players are blind (maintainer estimate, unverified). The game is fully playable by ear. That is a design requirement, not a favor. Accessibility problems are bugs.

The switch

Players turn the mode on with:

config screenreader yes

The in-game article is help 2132 (help screenreader). It lists what the mode changes:

  • Items and mobs lose the English name in parentheses.
  • Colored items get an aura word such as (Bright) in place of the color.
  • Random weapons show tier words.
  • Most ASCII pseudo-graphics become text descriptions.
  • After each experience gain the game says how many points remain to the next level.

Details of the setting:

  • It is stored for the account, not per character. The account-wide settings are screenreader, fightspam, skillspam and noweaponspam (config_is_account_wide in plug-ins/comm/configs.cpp).
  • A connection that identifies its terminal type as Lyntin counts as a screen reader even with the setting off.
  • The config entry in dreamland_world/config/settings.json carries the label and hint in all three languages.

Players are also advised to turn colors off (config colour no), since color codes add nothing for speech.

Branch in code

In C++, ask the engine:

#include "screenreader.h"

if (uses_screenreader(ch)) {
    // linear text
} else {
    // table, bar, drawing
}

Many places treat "screen reader on" and "colors off" the same. A viewer with color off cannot see a color cue either, so the pattern is:

if (!uses_screenreader(ch) && ch->getPC()->getConfig().color)
    return; // the colors are enough, say nothing extra

format_screenreader_flags and look_targets_hint in plug-ins/comm/look.cpp follow it.

In Fenia, read the character field. It already folds in the terminal check:

if (ch.screenreader)
    ch.ptc("Linear text.");
else
    ch.ptc("A frame or a table.");

The field is read-only. The raw bit is also available as .tables.config_flags.screenreader, but it misses the terminal-type case, so prefer ch.screenreader. ch.ptc(msg) prints a raw string to a character. Look other methods up through Fenia-API.

What the engine already does

Use these as models.

Place Plain-text twin
Item names in rooms and inventory (look.cpp) A color-coded tier becomes a word: (Legendary), (Epic), (Rare), (Uncommon), (Bright)
Jeweler sockets (look.cpp) Colored stars on the name become (Socketed)
Room descriptions (look.cpp) A list of the extra descriptions you can examine, including hidden ones
Social list (csocials.cpp) One social per line instead of a table
Calendar (weather.cpp) Today's date, active bonuses and upcoming bonuses as sentences instead of a grid with a legend
Mudlet package The side panel can be hidden with dl hide, and the game text carries every fact on its own

Visibility tags

Inside any string a player reads, a span can be limited to a kind of viewer. Each span is closed with {Ix.

Tag Shown to
{Is Screen reader users only
{IS Everyone who is not using a screen reader
{Iw Web client only
{IW Telnet only
{Ii Immortals only

A span prints when the viewer has the matching bit, so the pair {IS (drawing) and {Is (words) gives each audience its own version.

The trap: {x is a total reset. It also clears the visibility state, so everything after a {x inside a span prints to every viewer. Two rules follow:

  • Keep color resets and other {x outside the span, and gate only the value.
  • Do not gate a line assembled by a helper that emits its own {x. The first {x ends the gate, and the rest of the line leaks to everyone.

A clickable link inside a span is closed with {hx, not {x. A link already behaves correctly for web and telnet, so wrap it only when the wording around it differs. More on tags: Text-Markup.

Rules for new content

  1. No fact lives only in color. Pair every color cue with a word, as the aura words above do.
  2. No ASCII art as the only path. Frames, bars, grids and maps get a screen reader branch that reads as sentences or one item per line.
  3. Prefer one item per line over a table. Speech engines read columns across, and the result is noise.
  4. Keep text short. Every extra line is one more thing to listen to.
  5. Never rely on a graphic panel alone. The Mudlet panel can be hidden and the game text still carries every fact. Follow that.
  6. Wiki and docs: every image gets alt text, and no information is carried only by an image.

Web client

The main text window of the web client is an aria-live="polite" region that announces additions only (terminal.jsx). Decorative layers (terminal-glow, terminal-flash) are aria-hidden. Keep new decorative elements hidden from assistive technology the same way, and give interactive controls labels.

Editing text without a mouse

The line editor (ed) works in a plain telnet stream and has no cursor addressing, so it works with a screen reader. In the web client, webedit opens a text buffer (unverified for screen reader users). The editor is documented on OLC-Line-Editor.

Test your change

  1. Log in over telnet or the web client.
  2. Run config screenreader yes and config colour no.
  3. Walk through the feature with the screen off, or with your speech engine running.
  4. Check that each message still makes sense read aloud once, with no leftover tag fragments.

See also

Clone this wiki locally