You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Concept: a self-voicing accessibility layer for Bright Nights
I am a blind player who has spent a long time playing MUDs, interactive fiction, ChoiceScript, and a handful of accessible roguelikes. Cataclysm has been on my radar for years but is currently unplayable for screen reader users. The wish itself is not new — DDA has open accessibility issues going back years, #55436 and #65577 among them, without movement. After a research session done with an AI coding assistant — both for reading through BN's existing source and for working out what an accessibility architecture could look like — I want to put the thinking on the table here.
I am posting this rather than building it myself. I would prefer to see someone in this community pick it up, since the people who already know the codebase will move faster than I would. If nothing happens with it, I will eventually reconsider. For now I want to find out whether there is interest, whether the technical premises hold up under scrutiny from people who actually maintain BN, and what I have gotten wrong.
Why self-voicing rather than screen-reader-compatibility
There are two distinct architectural approaches and they are often confused.
Screen-reader compatibility means structuring every UI element so that an external screen reader can find the text through platform accessibility APIs — UI Automation on Windows, AT-SPI on Linux, NSAccessibility on macOS. For a SDL2-rendered game this means publishing accessibility providers per platform and maintaining them against ongoing UI churn. DDA's SCREEN_READER_MODE is the small slice of this approach and after roughly two years it covers exactly one screen.
Self-voicing means the game itself decides what to speak and routes that text through a TTS engine or screen reader output channel. No accessibility tree, no per-platform parallel UI representation. The game already has the data the player needs to hear; it just needs to choose what to vocalize and route it out. This is the architecture that Stardew Access uses for Stardew Valley and that Factorio Access uses for Factorio. It is the approach that has actually produced sustained playability for blind players in modded games, and it is what Cataclysm should use too.
The fact that Cataclysm is turn-based makes self-voicing dramatically easier than it is for real-time games. There is no tempo pressure on speech output; the player can take however long they need to read what is announced before deciding their next move. This is one of the key reasons BN is more tractable than its complexity suggests.
What the architecture would look like
A clean separation. C++ does the wiring; Lua does the behaviour.
The C++ side adds a small set of bindings:
A speech binding, gapi.speak(text), routing through SRAL — a cross-platform screen reader abstraction layer that auto-detects NVDA, JAWS, ZDSR, and Microsoft Narrator on Windows with SAPI fallback, uses VoiceOver and AVSpeech on macOS, and Speech Dispatcher on Linux. SRAL is MIT-licensed and is in production use in Sunless Sea Access. Roughly a hundred lines for the binding itself.
A clipboard binding, gapi.set_clipboard(text), with per-platform implementations for the Windows Clipboard API, X11 or Wayland clipboard on Linux, and NSPasteboard on macOS. A handful of lines per platform. This serves as a third reading channel for content the player wants to read at length in their own text environment, alongside speech for real-time events and braille (which SRAL handles automatically through whichever screen reader is detected).
Two additional Character bindings, consume and reload, because without them a blind player cannot eat or shoot. The existing Lua API exposes wear, take off, wield, unwield, drop, but consume and reload sit behind C++ menus that Lua cannot intercept. These are small wrappers over functions the game already has internally.
An on_add_msg hook in messages.cpp would be a useful early addition to make message vocalization event-driven rather than poll-based. A few lines following BN's existing cata::run_hooks convention. Polling would work as a starting point.
The Lua side does everything else: deciding what gets spoken with what priority, custom commands bound to hotkeys for surroundings inspection and character state, replacement menus where the C++ versions are not accessible, persistence of user preferences through game.mod_storage. Crucially, BN's existing keybinding system is already fully reconfigurable — accessibility commands register as proper game actions through gapi.register_action_menu_entry rather than requiring input-interception trickery.
The C++ work cannot be avoided by writing more Lua, because BN's Lua sandbox exposes only base, package, math, string, and table — no os, no io, no file or process access. A Lua mod can know what should be spoken; it cannot speak. The bridge from "knowing what to say" to "actually saying it" is the one thing that has to live in C++. Everything else can stay in script.
There is a side effect of this architecture worth pointing out. A speech-driven interface for some BN systems could be more efficient than the visual original even for sighted players. Vehicle construction is the clearest case: the underlying data is a clean list of mount points with coordinates and a list of available parts with installation requirements, and a text-driven interface that lets the player filter by part category, by mount point, by required tools, by weight class would be faster than dragging components onto a visual grid. The same applies to crafting, where a filterable speech-or-text-native browser over the recipe database can outpace scrolling through visual menus. This is not the primary justification for the project, but it is a reason to expect that some of the work would land as quality-of-life improvements rather than accessibility-only features.
What helps that other accessibility-mod projects do not get for free
Three BN-specific factors meaningfully reduce the scope of work:
The fully reconfigurable keybinding system already exists. Stardew Access, Factorio Access, and other accessibility mods have to fight their host games' input layers; BN's input layer welcomes new actions.
The in-game automation features — auto-pickup, auto-pulping, auto-forage, the Zone Manager — handle the routine work that a blind player would otherwise need keystroke-by-keystroke accessibility for. The accessibility mod's job is the irreducible interactive parts.
The Lua iteration loop, with the in-game console and gdebug.reload_lua_code(), means the day-to-day work runs at hot-reload speed. C++ changes are concentrated in the bridge layer and happen rarely.
Distribution
A fork is the cleanest path. The C++ work cannot ship as a mod, and BN's CC BY-SA 3.0 license explicitly permits forking and binary redistribution. BN's existing build.yml already builds for Windows MSVC, Linux Tiles and Curses, macOS Tiles and Curses on Intel and Apple Silicon, and Android, so the fork inherits the full build pipeline.
Whether the C++ bindings themselves are eventually offered as upstream PRs is a separate decision. The maintainers — chaosvolt, scarf005, and the Lua-binding contributors of the past year — have a strong track record of merging Lua-binding PRs quickly, and BN already ships a developer doc at docs/en/dev/explanation/accessibility.md plus has cursor-positioning calls in game.cpp, messages.cpp, and overmap_ui.cpp labeled "for screen readers". Self-voicing is a different strategy from what that document describes, but it fills a gap rather than competing.
Why this is now feasible at all
Eight years of open accessibility issues against Cataclysm without a serious effort are a real signal. The project is large, the codebase is C++, and a single-person effort against it has historically not been realistic. What has changed is the role AI coding assistants can play, and the change is on two distinct axes that matter independently.
The first is amplification of new work — writing C++ bindings, writing Lua mods, drafting build scripts, debugging cross-platform issues. This is the obvious capability and it is what most discussions of "AI in development" focus on.
The second is analysis of existing code, and this is the one that actually unlocks accessibility work for Cataclysm specifically. Navigating a half-million-line C++ codebase to understand how avatar_action::reload interacts with the inventory UI, or where messages flow through messages.cpp, or what the right integration point is for a speech hook — that kind of orientation work used to be the dominant cost of contributing to a project this size. With an AI assistant reading the source alongside, that cost drops by a large factor. The Lua API inventory above, the identification of where consume and reload sit, the location of the screen-reader cursor-positioning calls in the existing code — all of that came out of AI-assisted reading of BN's source.
Both capabilities matter. Without the second one, this post does not exist, because writing it required understanding what BN already exposes versus what would need to be added. I would expect anyone who actually picks up this work to use AI for both — to read the code with them and to write the code with them. That is the honest reason to believe the project is feasible for a small team or even a motivated individual now, when it was not in 2018 or 2022.
Open questions for the community
A few things I cannot answer from outside the project and would value input on.
First, on SRAL specifically: would BN's audio stack share a process cleanly with SRAL's per-platform paths, particularly on Linux where Speech Dispatcher manages its own audio session? I do not know whether anyone has tested this combination.
Second, on the Lua bindings: is Character::reload_with(gun, ammo) a sensible new variant for skipping the C++ ammo-selection menu, or is there a cleaner pattern that avoids duplicating logic from avatar_action::reload?
Third, on upstreaming: would the maintainers be open in principle to a PR that adds the speech binding, clipboard binding, and on_add_msg hook to BN's Lua API after they have proven themselves in a fork? The accessibility-specific Lua mod itself is opinionated and would stay in the fork or registry permanently, but the bindings would benefit any Lua mod author.
Fourth, on prior art I might have missed: is there any earlier Cataclysm accessibility attempt I should be aware of? My research turned up DDA issue #55436, DDA issue #65577, DDA issue #26885 from 2018, and nothing else. If someone else already started on this and stalled, I would rather build on their work than restart.
Reactions, corrections, and disagreement all welcome. I am posting this here specifically because the people who would best know whether the technical premises hold up are the ones who already work on BN.
If you are a Lua or C++ contributor in this community and any of this looks worth picking up — partially, fully, or as inspiration for a different approach — I would love to hear from you.
Greetings, Anouk.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Concept: a self-voicing accessibility layer for Bright Nights
I am a blind player who has spent a long time playing MUDs, interactive fiction, ChoiceScript, and a handful of accessible roguelikes. Cataclysm has been on my radar for years but is currently unplayable for screen reader users. The wish itself is not new — DDA has open accessibility issues going back years, #55436 and #65577 among them, without movement. After a research session done with an AI coding assistant — both for reading through BN's existing source and for working out what an accessibility architecture could look like — I want to put the thinking on the table here.
I am posting this rather than building it myself. I would prefer to see someone in this community pick it up, since the people who already know the codebase will move faster than I would. If nothing happens with it, I will eventually reconsider. For now I want to find out whether there is interest, whether the technical premises hold up under scrutiny from people who actually maintain BN, and what I have gotten wrong.
Why self-voicing rather than screen-reader-compatibility
There are two distinct architectural approaches and they are often confused.
Screen-reader compatibility means structuring every UI element so that an external screen reader can find the text through platform accessibility APIs — UI Automation on Windows, AT-SPI on Linux, NSAccessibility on macOS. For a SDL2-rendered game this means publishing accessibility providers per platform and maintaining them against ongoing UI churn. DDA's SCREEN_READER_MODE is the small slice of this approach and after roughly two years it covers exactly one screen.
Self-voicing means the game itself decides what to speak and routes that text through a TTS engine or screen reader output channel. No accessibility tree, no per-platform parallel UI representation. The game already has the data the player needs to hear; it just needs to choose what to vocalize and route it out. This is the architecture that Stardew Access uses for Stardew Valley and that Factorio Access uses for Factorio. It is the approach that has actually produced sustained playability for blind players in modded games, and it is what Cataclysm should use too.
The fact that Cataclysm is turn-based makes self-voicing dramatically easier than it is for real-time games. There is no tempo pressure on speech output; the player can take however long they need to read what is announced before deciding their next move. This is one of the key reasons BN is more tractable than its complexity suggests.
What the architecture would look like
A clean separation. C++ does the wiring; Lua does the behaviour.
The C++ side adds a small set of bindings:
A speech binding,
gapi.speak(text), routing through SRAL — a cross-platform screen reader abstraction layer that auto-detects NVDA, JAWS, ZDSR, and Microsoft Narrator on Windows with SAPI fallback, uses VoiceOver and AVSpeech on macOS, and Speech Dispatcher on Linux. SRAL is MIT-licensed and is in production use in Sunless Sea Access. Roughly a hundred lines for the binding itself.A clipboard binding,
gapi.set_clipboard(text), with per-platform implementations for the Windows Clipboard API, X11 or Wayland clipboard on Linux, and NSPasteboard on macOS. A handful of lines per platform. This serves as a third reading channel for content the player wants to read at length in their own text environment, alongside speech for real-time events and braille (which SRAL handles automatically through whichever screen reader is detected).Two additional Character bindings,
consumeandreload, because without them a blind player cannot eat or shoot. The existing Lua API exposes wear, take off, wield, unwield, drop, but consume and reload sit behind C++ menus that Lua cannot intercept. These are small wrappers over functions the game already has internally.An
on_add_msghook inmessages.cppwould be a useful early addition to make message vocalization event-driven rather than poll-based. A few lines following BN's existingcata::run_hooksconvention. Polling would work as a starting point.The Lua side does everything else: deciding what gets spoken with what priority, custom commands bound to hotkeys for surroundings inspection and character state, replacement menus where the C++ versions are not accessible, persistence of user preferences through
game.mod_storage. Crucially, BN's existing keybinding system is already fully reconfigurable — accessibility commands register as proper game actions throughgapi.register_action_menu_entryrather than requiring input-interception trickery.The C++ work cannot be avoided by writing more Lua, because BN's Lua sandbox exposes only
base,package,math,string, andtable— noos, noio, no file or process access. A Lua mod can know what should be spoken; it cannot speak. The bridge from "knowing what to say" to "actually saying it" is the one thing that has to live in C++. Everything else can stay in script.There is a side effect of this architecture worth pointing out. A speech-driven interface for some BN systems could be more efficient than the visual original even for sighted players. Vehicle construction is the clearest case: the underlying data is a clean list of mount points with coordinates and a list of available parts with installation requirements, and a text-driven interface that lets the player filter by part category, by mount point, by required tools, by weight class would be faster than dragging components onto a visual grid. The same applies to crafting, where a filterable speech-or-text-native browser over the recipe database can outpace scrolling through visual menus. This is not the primary justification for the project, but it is a reason to expect that some of the work would land as quality-of-life improvements rather than accessibility-only features.
What helps that other accessibility-mod projects do not get for free
Three BN-specific factors meaningfully reduce the scope of work:
The fully reconfigurable keybinding system already exists. Stardew Access, Factorio Access, and other accessibility mods have to fight their host games' input layers; BN's input layer welcomes new actions.
The in-game automation features — auto-pickup, auto-pulping, auto-forage, the Zone Manager — handle the routine work that a blind player would otherwise need keystroke-by-keystroke accessibility for. The accessibility mod's job is the irreducible interactive parts.
The Lua iteration loop, with the in-game console and
gdebug.reload_lua_code(), means the day-to-day work runs at hot-reload speed. C++ changes are concentrated in the bridge layer and happen rarely.Distribution
A fork is the cleanest path. The C++ work cannot ship as a mod, and BN's CC BY-SA 3.0 license explicitly permits forking and binary redistribution. BN's existing
build.ymlalready builds for Windows MSVC, Linux Tiles and Curses, macOS Tiles and Curses on Intel and Apple Silicon, and Android, so the fork inherits the full build pipeline.Whether the C++ bindings themselves are eventually offered as upstream PRs is a separate decision. The maintainers — chaosvolt, scarf005, and the Lua-binding contributors of the past year — have a strong track record of merging Lua-binding PRs quickly, and BN already ships a developer doc at
docs/en/dev/explanation/accessibility.mdplus has cursor-positioning calls ingame.cpp,messages.cpp, andovermap_ui.cpplabeled "for screen readers". Self-voicing is a different strategy from what that document describes, but it fills a gap rather than competing.Why this is now feasible at all
Eight years of open accessibility issues against Cataclysm without a serious effort are a real signal. The project is large, the codebase is C++, and a single-person effort against it has historically not been realistic. What has changed is the role AI coding assistants can play, and the change is on two distinct axes that matter independently.
The first is amplification of new work — writing C++ bindings, writing Lua mods, drafting build scripts, debugging cross-platform issues. This is the obvious capability and it is what most discussions of "AI in development" focus on.
The second is analysis of existing code, and this is the one that actually unlocks accessibility work for Cataclysm specifically. Navigating a half-million-line C++ codebase to understand how
avatar_action::reloadinteracts with the inventory UI, or where messages flow throughmessages.cpp, or what the right integration point is for a speech hook — that kind of orientation work used to be the dominant cost of contributing to a project this size. With an AI assistant reading the source alongside, that cost drops by a large factor. The Lua API inventory above, the identification of whereconsumeandreloadsit, the location of the screen-reader cursor-positioning calls in the existing code — all of that came out of AI-assisted reading of BN's source.Both capabilities matter. Without the second one, this post does not exist, because writing it required understanding what BN already exposes versus what would need to be added. I would expect anyone who actually picks up this work to use AI for both — to read the code with them and to write the code with them. That is the honest reason to believe the project is feasible for a small team or even a motivated individual now, when it was not in 2018 or 2022.
Open questions for the community
A few things I cannot answer from outside the project and would value input on.
First, on SRAL specifically: would BN's audio stack share a process cleanly with SRAL's per-platform paths, particularly on Linux where Speech Dispatcher manages its own audio session? I do not know whether anyone has tested this combination.
Second, on the Lua bindings: is
Character::reload_with(gun, ammo)a sensible new variant for skipping the C++ ammo-selection menu, or is there a cleaner pattern that avoids duplicating logic fromavatar_action::reload?Third, on upstreaming: would the maintainers be open in principle to a PR that adds the speech binding, clipboard binding, and
on_add_msghook to BN's Lua API after they have proven themselves in a fork? The accessibility-specific Lua mod itself is opinionated and would stay in the fork or registry permanently, but the bindings would benefit any Lua mod author.Fourth, on prior art I might have missed: is there any earlier Cataclysm accessibility attempt I should be aware of? My research turned up DDA issue #55436, DDA issue #65577, DDA issue #26885 from 2018, and nothing else. If someone else already started on this and stalled, I would rather build on their work than restart.
Reactions, corrections, and disagreement all welcome. I am posting this here specifically because the people who would best know whether the technical premises hold up are the ones who already work on BN.
If you are a Lua or C++ contributor in this community and any of this looks worth picking up — partially, fully, or as inspiration for a different approach — I would love to hear from you.
Greetings, Anouk.
All reactions