A screen-reader-first mathematical accessibility toolkit for converting LaTeX, MathML, and plaintext/Unicode math into speech, Braille, and navigable ARIA structures.
Quick Start β’ Features β’ Installation β’ Usage β’ API Reference β’ Contributing
Accessible Math Reader (AMR) is a Python package and web application that makes mathematical notation accessible to visually impaired users. It parses LaTeX, MathML, and plaintext/Unicode math expressions and converts them into:
- Natural language speech with configurable verbosity (verbose, concise, superbrief)
- Braille notation in both Nemeth (US standard) and UEB (international standard)
- Audio files via Google Text-to-Speech (gTTS)
- ARIA-annotated HTML for keyboard-navigable, screen-reader-friendly exploration
AMR can be used as a Python library, a CLI tool, or through a Flask-based web interface.
| Category | Highlights |
|---|---|
| π Multi-Format Input | Parse LaTeX, MathML, and plaintext/Unicode math expressions with auto-detection |
| π Speech Output | Natural language descriptions with 3 verbosity levels and SSML support |
| β Ώ Braille Support | Full Nemeth Braille Code and Unified English Braille (UEB) converters |
| βΏ ARIA Navigation | Keyboard-accessible, step-by-step expression exploration with 3 navigation modes |
| π Multi-Format Clipboard | Copy formulas as LaTeX, accessible text, or Braille from the web UI |
| π¨ Accessible Web UI | Dark/light themes, high-contrast mode, zoom controls, screen-reader optimized |
| π Plugin System | Extensible architecture for custom speech rules, Braille notations, and input formats |
| β¨οΈ CLI Tool | Full-featured command-line interface with interactive and batch modes |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Accessible Math Reader β
ββββββββββββ¬βββββββββββ¬ββββββββββββββββββββββββββββββββ¬ββββββββββββββββ€
β CLI β Web UI β Python API (MathReader) β Plugins β
β (amr) β (Flask) β β (extensible)β
ββββββββββββ΄βββββββββββ΄ββββββββββββββββββββββββββββββββ΄ββββββββββββββββ€
β Core Pipeline β
β ββββββββββββ ββββββββββββββββ ββββββββββββββββββββββββββββββ β
β β Parser ββββΈβ Semantic AST ββββΈβ Renderers β β
β β LaTeX β β (SemanticNodeβ β βββββββββββ¬βββββββββββββ β β
β β MathML β β NodeType) β β β Speech β Braille β β β
β β Plaintextβ
β ββββββββββββ ββββββββ¬ββββββββ β β Engine β Nemeth/UEB β β β
β β β βββββββββββ΄βββββββββββββ β β
β βΌ β βββββββββββ¬βββββββββββββ β β
β βββββββββββββ β β ARIA β Simple β β β
β β Navigator β β βRenderer β Text β β β
β β (keyboard β β βββββββββββ΄βββββββββββββ β β
β β explore) β ββββββββββββββββββββββββββββββ β
β βββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Configuration β
β Config Β· SpeechConfig Β· BrailleConfig Β· A11yConfig β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
accessible-math-reader/
βββ accessible_math_reader/ # π¦ Installable Python package
β βββ __init__.py # Public API exports
β βββ reader.py # MathReader β high-level unified interface
β βββ cli.py # CLI entry point (`amr` command)
β βββ config.py # Configuration management (env, file, API)
β βββ core/ # Core parsing & rendering engine
β β βββ parser.py # LaTeX / MathML / Plaintext β Semantic AST
β β βββ semantic.py # SemanticNode, NodeType, MathNavigator
β β βββ renderer.py # Base rendering infrastructure
β β βββ aria_navigator.py # ARIA-enhanced keyboard navigation
β β βββ aria_renderer.py # Accessible HTML generation
β β βββ accessibility_contract.py # Accessibility validation contracts
β βββ speech/ # Speech output subsystem
β β βββ engine.py # TTS engine abstraction (gTTS backend)
β β βββ rules.py # Verbosity-based speech rules
β βββ braille/ # Braille conversion subsystem
β β βββ nemeth.py # Nemeth Braille Code converter
β β βββ ueb.py # Unified English Braille converter
β βββ plugins/ # Plugin system
β βββ base.py # Abstract plugin base classes
βββ app.py # π Flask web application entry point
βββ src/ # Legacy web-app helper modules
β βββ latex_parser.py # Regex-based LaTeX parser (for Flask UI)
β βββ braille_converter.py # Simple character-level Braille mapper
β βββ speech_converter.py # gTTS wrapper for web UI
βββ templates/
β βββ index.html # π₯οΈ Web UI template (dark/light, accessible)
βββ static/
β βββ css/style.css # Web UI styles
β βββ js/
β βββ app.js # Frontend logic & keyboard shortcuts
β βββ clipboard.js # Multi-format clipboard support
βββ docs/ # π Documentation
β βββ api.md # Full Python API reference
β βββ input-formats.md # Supported LaTeX & MathML syntax
β βββ accessibility.md # Screen reader, Braille & ARIA guide
β βββ configuration.md # Configuration reference
β βββ examples.md # Code samples & use cases
βββ output/ # Sample Braille output files (.brf)
βββ pyproject.toml # Package metadata & build config
βββ requirements.txt # Web-app-specific dependencies
βββ LICENSE # MIT License
Install directly from the repository:
# Clone the repository
git clone https://github.com/AndyFerns/Accessible-Math-Reader.git
cd Accessible-Math-Reader
# Create and activate a virtual environment
python -m venv venv
# Windows
venv\Scripts\activate
# Linux / macOS
source venv/bin/activate
# Install the package in editable mode (with all extras)
pip install -e ".[dev,web]"This installs AMR as a package and registers the amr CLI command.
If you only need the Python API (no web UI, no dev tools):
pip install -e .Core dependencies are just gtts and lxml.
pip install accessible-math-readerAMR provides three interfaces: a Python API, a CLI tool, and a web UI.
from accessible_math_reader import MathReader
reader = MathReader()
# ββ Speech ββββββββββββββββββββββββββββββββββββββββββ
speech = reader.to_speech(r"\frac{a}{b}")
print(speech)
# β "start fraction a over b end fraction"
# ββ Braille (Nemeth) ββββββββββββββββββββββββββββββββ
braille = reader.to_braille(r"\frac{a}{b}", notation="nemeth")
print(braille)
# β "β Ήβ β β β Ό"
# ββ Braille (UEB) ββββββββββββββββββββββββββββββββββ
ueb = reader.to_braille(r"\frac{a}{b}", notation="ueb")
# ββ Audio file ββββββββββββββββββββββββββββββββββββββ
reader.to_audio(r"\frac{a}{b}", "output.mp3")
# ββ SSML markup βββββββββββββββββββββββββββββββββββββ
ssml = reader.to_ssml(r"\sqrt{x}")
# ββ Semantic tree inspection ββββββββββββββββββββββββ
structure = reader.get_structure(r"\frac{a+b}{c}")
# ββ Plaintext / Unicode math ββββββββββββββββββββββββ
speech = reader.to_speech("xΒ² + yΒ² = zΒ²")
speech = reader.to_speech("(a+b)/(c-d)")
speech = reader.to_speech("sqrt(x) + Ο")from accessible_math_reader import MathReader, VerbosityLevel
reader = MathReader()
reader.set_verbosity(VerbosityLevel.VERBOSE)
reader.to_speech(r"\frac{a}{b}") # "start fraction a over b end fraction"
reader.set_verbosity(VerbosityLevel.CONCISE)
reader.to_speech(r"\frac{a}{b}") # "a over b"
reader.set_verbosity(VerbosityLevel.SUPERBRIEF)
reader.to_speech(r"\frac{a}{b}") # "fraction a b"from accessible_math_reader import MathReader, Config
from accessible_math_reader.config import SpeechConfig, BrailleConfig, SpeechStyle, BrailleNotation
config = Config(
speech=SpeechConfig(style=SpeechStyle.CONCISE, language="en", rate=0.9),
braille=BrailleConfig(notation=BrailleNotation.UEB),
)
reader = MathReader(config)nav = reader.get_navigator(r"\frac{a+b}{c}")
nav.enter() # Drill into the fraction
nav.next() # Move to the next sibling
nav.exit() # Go back up to the parent
path = nav.get_path() # Breadcrumb trail from rootAfter installing the package, the amr command is available system-wide.
# Speech output (default)
amr "\frac{a^2 + b^2}{c}"
# Plaintext / Unicode math
amr "xΒ² + yΒ² = zΒ²"
amr "(a+b)/(c-d)"
amr "sqrt(x) + Ο"
# Braille output (Nemeth)
amr --braille "\frac{a}{b}"
# Braille output (UEB)
amr --braille --notation ueb "\sqrt{x}"
# Generate audio file
amr --audio output.mp3 "\frac{1}{2}"
# SSML output
amr --ssml "\frac{a}{b}"
# JSON output (speech + braille + structure)
amr --json "\frac{a}{b}"
# Show expression tree structure
amr --structure "\frac{a+b}{c}"
# Set verbosity
amr --verbosity concise "x^2 + y^2 = z^2"
# Read from file (one expression per line)
amr --input equations.txt
# Write output to file
amr --output results.txt "\frac{a}{b}"
# Interactive REPL mode
amr --interactive| Command | Action |
|---|---|
:quit / :exit |
Exit interactive mode |
:verbosity <level> |
Set verbosity (verbose, concise, superbrief) |
:braille |
Switch to Braille output |
:speech |
Switch to speech output |
:help |
Show all commands |
The web UI provides a visual, accessible interface built with Flask:
# Install web dependencies
pip install -e ".[web]"
# Start the development server
python app.py
# Open http://localhost:5000Web UI Features:
- LaTeX / MathML / plaintext/Unicode input with live conversion
- Tabbed output: Formula preview, Speech text, Braille, Accessible ARIA view
- Multi-format clipboard (copy as LaTeX, plain text, or Braille)
- Dark / Light / High-Contrast themes
- Full keyboard navigation (
Ctrl+Enterto convert,Alt+1β4to switch tabs) - Screen-reader optimized with ARIA live regions
AMR supports three configuration methods (highest priority first):
# Linux / macOS
export AMR_SPEECH_STYLE=concise # verbose | concise | superbrief
export AMR_BRAILLE_NOTATION=nemeth # nemeth | ueb
export AMR_SPEECH_LANGUAGE=en # Language code
export AMR_PLUGIN_DIRS=/path/to/plugins
# Windows PowerShell
$env:AMR_SPEECH_STYLE = "concise"
$env:AMR_BRAILLE_NOTATION = "ueb"{
"speech": {
"style": "verbose",
"language": "en",
"rate": 1.0,
"announce_structure": true
},
"braille": {
"notation": "nemeth",
"include_indicators": true
},
"accessibility": {
"step_by_step": true,
"announce_errors": true,
"highlight_current": true
}
}config = Config.from_file("amr-config.json")
reader = MathReader(config)config = Config()
config.speech.style = SpeechStyle.CONCISE
config.save("my-config.json")π Full configuration reference: docs/configuration.md
AMR's plugin architecture supports four extension points:
| Plugin Type | Base Class | Purpose |
|---|---|---|
| Speech Rules | SpeechRulesPlugin |
Custom verbalization rules for math constructs |
| Braille Notation | BrailleNotationPlugin |
Additional Braille systems (French, German, etc.) |
| Input Format | InputFormatPlugin |
New input parsers (AsciiMath, MathJSON, etc.) |
| Localization | BasePlugin |
Language/locale-specific adaptations |
from accessible_math_reader.plugins.base import SpeechRulesPlugin, PluginInfo, PluginType
class MyCustomRules(SpeechRulesPlugin):
@property
def info(self):
return PluginInfo(
name="my-rules",
version="1.0.0",
description="Custom speech rules",
author="Your Name",
plugin_type=PluginType.SPEECH_RULES,
)
def get_speech_rules(self):
return {"FRACTION": lambda node: "custom fraction rendering"}| Document | Description |
|---|---|
| API Reference | Full Python API with code examples |
| Input Formats | Supported LaTeX commands and MathML elements |
| Accessibility Guide | Screen reader, Braille display, and ARIA features |
| Configuration | All speech, Braille, and accessibility settings |
| Examples | Common use cases, batch processing, and web integration |
This project uses MkDocs with the Material theme for documentation hosting:
# Install documentation dependencies
pip install mkdocs mkdocs-material mkdocstrings[python]
# Serve docs locally (live reload)
mkdocs serve
# Build static site
mkdocs buildSee mkdocs.yml for the full configuration.
| Standard | Status |
|---|---|
| WCAG 2.2 AA | β Full compliance for web interface |
| WAI-ARIA 1.2 | β Proper roles, labels, live regions, roving tabindex |
| Screen Readers | β Tested with NVDA, JAWS, VoiceOver, Narrator |
| Keyboard Navigation | β Full keyboard access, visible focus indicators |
| Braille Displays | β Unicode Braille output compatible with refreshable displays |
- Python 3.9+
- pip
git clone https://github.com/AndyFerns/Accessible-Math-Reader.git
cd Accessible-Math-Reader
python -m venv venv
venv\Scripts\activate # Windows
# source venv/bin/activate # Linux / macOS
pip install -e ".[dev,web]"# Run full test suite with coverage
pytest tests/ -v --cov=accessible_math_reader --cov-report=term-missing
# Run a specific test file
pytest tests/test_parser.py -v# Run ruff linter
ruff check accessible_math_reader/
# Auto-fix lint issues
ruff check accessible_math_reader/ --fixpython app.py
# Open http://localhost:5000We welcome contributions! Here's how to get started:
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature - Install dev dependencies:
pip install -e ".[dev]" - Make your changes and add tests
- Run the linter:
ruff check accessible_math_reader/ - Run the tests:
pytest tests/ -v - Commit with a descriptive message
- Push to your fork and open a Pull Request
- π Localization: Speech rules for other languages
- β Ώ Braille: Additional Braille notation systems (French, German, etc.)
- π§ͺ Testing: More unit tests, especially for edge-case math expressions
- βΏ User Testing: Feedback from blind and low-vision users
- π Documentation: Tutorials, guides, and examples
# Install build tool
pip install build
# Build source distribution and wheel
python -m build
# Output will be in dist/
# dist/accessible_math_reader-0.1.0.tar.gz
# dist/accessible_math_reader-0.1.0-py3-none-any.whl# Install twine
pip install twine
# Upload to TestPyPI (for testing)
twine upload --repository testpypi dist/*
# Upload to PyPI (production)
twine upload dist/*# From the wheel file
pip install dist/accessible_math_reader-0.1.0-py3-none-any.whl
# From TestPyPI
pip install --index-url https://test.pypi.org/simple/ accessible-math-reader
# From PyPI (once published)
pip install accessible-math-readerThis project is licensed under the MIT License β see the LICENSE file for details.
- gTTS β Google Text-to-Speech engine
- lxml β XML/MathML parsing
- Flask β Web framework
- WCAG β Accessibility guidelines
- Nemeth Braille Code β Mathematical Braille standard
- UEB β Unified English Braille
Built with βΏ accessibility as a first-class priority.