Screen-reader-first math accessibility toolkit for converting LaTeX/MathML to speech and Braille. Developed under FCRIT Vashi.
Project description
♿ Accessible Math Reader
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
📖 Overview
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.
🎓 Developed at FCRIT Vashi as a project in academic research in accessibility and inclusive technology.
✨ Features
| 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 |
🏗️ Architecture
┌─────────────────────────────────────────────────────────────────────┐
│ 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 │
└─────────────────────────────────────────────────────────────────────┘
Project Structure
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
🚀 Installation
As a Python Package (Recommended)
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.
Minimal Install (Library Only)
If you only need the Python API (no web UI, no dev tools):
pip install -e .
Core dependencies are just gtts and lxml.
From PyPI (coming soon)
pip install accessible-math-reader
📋 Usage
AMR provides three interfaces: a Python API, a CLI tool, and a web UI.
1. Python API
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) + π")
Changing Verbosity
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"
Custom Configuration
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)
Step-by-Step Navigation
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 root
2. Command-Line Interface (amr)
After 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
Interactive Mode Commands
| 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 |
3. Web Interface
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:5000
Web 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
⚙️ Configuration
AMR supports three configuration methods (highest priority first):
1. Environment Variables
# 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"
2. JSON Config File
{
"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)
3. Python API
config = Config()
config.speech.style = SpeechStyle.CONCISE
config.save("my-config.json")
📖 Full configuration reference: docs/configuration.md
🔌 Plugin System
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"}
📚 Documentation
| 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 |
Generating Documentation Locally
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 build
See mkdocs.yml for the full configuration.
🎯 Accessibility Compliance
| 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 |
🧑💻 Development
Prerequisites
- Python 3.9+
- pip
Setup
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]"
Running Tests
# 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
Linting
# Run ruff linter
ruff check accessible_math_reader/
# Auto-fix lint issues
ruff check accessible_math_reader/ --fix
Running the Web Server
python app.py
# Open http://localhost:5000
🤝 Contributing
We 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
Areas Where Help Is Needed
- 🌍 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
📦 Releasing as a Package
Building the Distribution
# 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
Publishing to PyPI
# Install twine
pip install twine
# Upload to TestPyPI (for testing)
twine upload --repository testpypi dist/*
# Upload to PyPI (production)
twine upload dist/*
Installing from the Built Package
# 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-reader
📄 License
This project is licensed under the MIT License — see the LICENSE file for details.
🙏 Acknowledgments
- 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
- FCRIT - Fr. Conceicao Rodrigues Institute of Technology
Built with ♿ accessibility as a first-class priority.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file accessible_math_reader-0.4.1.tar.gz.
File metadata
- Download URL: accessible_math_reader-0.4.1.tar.gz
- Upload date:
- Size: 115.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2ce12e43868a1c9047e9a5a4d5e28429ec5fdc9ed9b978c53613e01ee5f879dc
|
|
| MD5 |
7761fb1f23c150edd5c2e825d3152219
|
|
| BLAKE2b-256 |
cae4e7d6fdbdb0e40938d8afee48844363fce2e7ff707957fd6c9d564c83e29e
|
File details
Details for the file accessible_math_reader-0.4.1-py3-none-any.whl.
File metadata
- Download URL: accessible_math_reader-0.4.1-py3-none-any.whl
- Upload date:
- Size: 91.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
98e09aaf71f72fe95de4d278a589f23d1d397cb0e4213453f821fd34f98532a6
|
|
| MD5 |
cd36e965835506290460c36fbbc908f2
|
|
| BLAKE2b-256 |
652ce4e10c04fe3370ea89d12a346ba16b82547fed5ba74b2c2ea20c1b98d64f
|