A comprehensive TypeScript library for processing AAC (Augmentative and Alternative Communication) file formats with advanced translation support, cross-format conversion, and robust error handling.
processTexts()npm install @willwade/aac-processors
git clone https://github.com/willwade/AACProcessors-nodejs.git
cd AACProcessors-nodejs
npm install
npm run build
better-sqlite3 is a native module and must be rebuilt against Electron's Node.js runtime. If you see a NODE_MODULE_VERSION mismatch error, rebuild after installing dependencies:
npm install
npx electron-rebuild
Or add a postinstall hook so the rebuild happens automatically:
{
"scripts": {
"postinstall": "electron-builder install-app-deps"
}
}
This step is only required for Electron apps; regular Node.js consumers do not need it.
C:\Users\Public\Documents\Smartbox\Grid 3\Users\{username}\{langCode}\Phrases\history.sqliteC:\Users\Public\Documents\Smartbox\Grid 3\Users\{username}\Grid Sets\C:\Users{username}\AppData\Roaming\Tobii Dynavox\Snap Scene\Users{userId} (.sps/.spb)import {
getProcessor,
DotProcessor,
SnapProcessor,
AstericsGridProcessor,
} from "aac-processors";
// Auto-detect processor by file extension
const processor = getProcessor("communication-board.dot");
const tree = processor.loadIntoTree("communication-board.dot");
// Extract all text content
const texts = processor.extractTexts("communication-board.dot");
console.log("Found texts:", texts);
// Direct processor usage
const dotProcessor = new DotProcessor();
const aacTree = dotProcessor.loadIntoTree("examples/example.dot");
console.log("Pages:", Object.keys(aacTree.pages).length);
const { getProcessor, DotProcessor } = require("aac-processors");
const processor = getProcessor("board.dot");
const tree = processor.loadIntoTree("board.dot");
console.log(tree);
AACProcessors includes an intelligent filtering system to handle navigation bars and system buttons that are common in AAC applications but may not be appropriate when converting between formats.
By default, the following buttons are filtered out during conversion:
import { GridsetProcessor } from "aac-processors";
// Default: exclude navigation/system buttons (recommended)
const processor = new GridsetProcessor();
// Preserve all buttons (legacy behavior)
const processor = new GridsetProcessor({ preserveAllButtons: true });
// Custom filtering
const processor = new GridsetProcessor({
excludeNavigationButtons: true,
excludeSystemButtons: false,
customButtonFilter: (button) => !button.label.includes("Settings"),
});
All processors support built-in translation via the processTexts() method:
import { DotProcessor } from "aac-processors";
const processor = new DotProcessor();
// 1. Extract all translatable text
const originalTexts = processor.extractTexts("board.dot");
// 2. Create translation map (integrate with your translation service)
const translations = new Map([
["Hello", "Hola"],
["Goodbye", "AdiΓ³s"],
["Food", "Comida"],
]);
// 3. Apply translations and save
const translatedBuffer = processor.processTexts(
"board.dot",
translations,
"board-spanish.dot",
);
console.log("Translation complete!");
The library includes an optional high-performance analytics engine for evaluating AAC board sets based on the AAC Effort Algorithm (v0.2).
For detailed documentation, see the AAC Metrics Guide and Vocabulary Analysis Guide.
import { ObfsetProcessor, Analytics } from "@willwade/aac-processors";
const processor = new ObfsetProcessor();
const tree = processor.loadIntoTree("my_pageset.obfset");
// Run clinical effort analysis
const result = new Analytics.MetricsCalculator().analyze(tree);
console.log(`Average Effort: ${result.total_words}`);
Validate AAC files against format specifications to ensure data integrity:
import { ObfProcessor, GridsetProcessor } from "aac-processors";
// Validate OBF/OBZ files
const obfProcessor = new ObfProcessor();
const result = await obfProcessor.validate("board.obf");
console.log(`Valid: ${result.valid}`);
console.log(`Errors: ${result.errors}`);
console.log(`Warnings: ${result.warnings}`);
// Detailed validation results
if (!result.valid) {
result.results
.filter((check) => !check.valid)
.forEach((check) => {
console.log(`β ${check.description}: ${check.error}`);
});
}
// Validate Gridset files (with optional password for encrypted files)
const gridsetProcessor = new GridsetProcessor({
gridsetPassword: "optional-password",
});
const gridsetResult = await gridsetProcessor.validate("vocab.gridsetx");
# Validate a file
aacprocessors validate board.obf
# JSON output
aacprocessors validate board.obf --json
# Quiet mode (just valid/invalid)
aacprocessors validate board.gridset --quiet
# Validate encrypted Gridset file
aacprocessors validate board.gridsetx --gridset-password <password>
OBF/OBZ: Spec compliance (Open Board Format)
Gridset: XML structure
Snap: Package structure
TouchChat: XML structure
Convert between any supported AAC formats:
import { DotProcessor, ObfProcessor } from "aac-processors";
// Load from DOT format
const dotProcessor = new DotProcessor();
const tree = dotProcessor.loadIntoTree("communication-board.dot");
// Save as OBF format
const obfProcessor = new ObfProcessor();
obfProcessor.saveFromTree(tree, "communication-board.obf");
// The tree structure is preserved across formats
console.log("Conversion complete!");
import { AstericsGridProcessor } from "aac-processors";
// Load Asterics Grid file with audio support
const processor = new AstericsGridProcessor({ loadAudio: true });
const tree = processor.loadIntoTree("communication-board.grd");
// Access audio recordings from buttons
tree.traverse((page) => {
page.buttons.forEach((button) => {
if (button.audioRecording) {
console.log(`Button "${button.label}" has audio recording`);
console.log(
`Audio data size: ${button.audioRecording.data?.length} bytes`,
);
}
});
});
// Add audio to specific elements
const audioData = Buffer.from(/* your audio data */);
processor.addAudioToElement(
"board.grd",
"element-id",
audioData,
JSON.stringify({ mimeType: "audio/wav", durationMs: 2000 }),
);
// Create enhanced version with multiple audio recordings
const audioMappings = new Map();
audioMappings.set("element-1", { audioData: audioBuffer1 });
audioMappings.set("element-2", { audioData: audioBuffer2 });
processor.createAudioEnhancedGridFile(
"source.grd",
"enhanced.grd",
audioMappings,
);
import { ExcelProcessor, getProcessor } from "aac-processors";
// Convert any AAC format to Excel for analysis
const sourceProcessor = getProcessor("communication-board.gridset");
const tree = sourceProcessor.loadIntoTree("communication-board.gridset");
// Export to Excel with visual styling and navigation
const excelProcessor = new ExcelProcessor();
excelProcessor.saveFromTree(tree, "vocabulary-analysis.xlsx");
// Each AAC page becomes an Excel worksheet tab
// Buttons are represented as cells with:
// - Cell value = button label
// - Cell background = button background color
// - Cell font color = button font color
// - Cell comments = button message/vocalization
// - Hyperlinks for navigation between worksheets
// Optional: Navigation row with standard AAC buttons
// (Home, Message Bar, Delete, Back, Clear) appears on each worksheet
import { AACTree, AACPage, AACButton } from "aac-processors";
// Create a communication board programmatically
const tree = new AACTree();
const homePage = new AACPage({
id: "home",
name: "Home Page",
buttons: [],
});
const helloButton = new AACButton({
id: "btn_hello",
label: "Hello",
message: "Hello, how are you?",
type: "SPEAK",
});
const foodButton = new AACButton({
id: "btn_food",
label: "Food",
message: "I want food",
type: "NAVIGATE",
targetPageId: "food_page",
});
homePage.addButton(helloButton);
homePage.addButton(foodButton);
tree.addPage(homePage);
// Save to any format
const processor = new DotProcessor();
processor.saveFromTree(tree, "my-board.dot");
import { DotProcessor } from "aac-processors";
const processor = new DotProcessor();
try {
const tree = processor.loadIntoTree("potentially-corrupted.dot");
console.log("Successfully loaded:", Object.keys(tree.pages).length, "pages");
} catch (error) {
console.error("Failed to load file:", error.message);
// Processor handles corruption gracefully and provides meaningful errors
}
The library now provides comprehensive styling support across all AAC formats, preserving visual appearance when converting between formats.
interface AACStyle {
backgroundColor?: string; // Button/page background color
fontColor?: string; // Text color
borderColor?: string; // Border color
borderWidth?: number; // Border thickness
fontSize?: number; // Font size in pixels
fontFamily?: string; // Font family name
fontWeight?: string; // "normal" | "bold"
fontStyle?: string; // "normal" | "italic"
textUnderline?: boolean; // Text underline
labelOnTop?: boolean; // Label position (TouchChat)
transparent?: boolean; // Transparent background
}
import { AACTree, AACPage, AACButton } from "aac-processors";
// Create a page with styling
const page = new AACPage({
id: "main-page",
name: "Main Communication Board",
grid: [],
buttons: [],
parentId: null,
style: {
backgroundColor: "#f0f8ff",
fontFamily: "Arial",
fontSize: 16,
},
});
// Create buttons with comprehensive styling
const speakButton = new AACButton({
id: "speak-btn-1",
label: "Hello",
message: "Hello, how are you?",
type: "SPEAK",
action: null,
style: {
backgroundColor: "#4CAF50",
fontColor: "#ffffff",
borderColor: "#45a049",
borderWidth: 2,
fontSize: 18,
fontFamily: "Helvetica",
fontWeight: "bold",
labelOnTop: true,
},
});
const navButton = new AACButton({
id: "nav-btn-1",
label: "More",
message: "Navigate to more options",
type: "NAVIGATE",
targetPageId: "more-page",
action: {
type: "NAVIGATE",
targetPageId: "more-page",
},
style: {
backgroundColor: "#2196F3",
fontColor: "#ffffff",
borderColor: "#1976D2",
borderWidth: 1,
fontSize: 16,
fontStyle: "italic",
transparent: false,
},
});
page.addButton(speakButton);
page.addButton(navButton);
const tree = new AACTree();
tree.addPage(page);
// Save with styling preserved
import { SnapProcessor } from "aac-processors";
const processor = new SnapProcessor();
processor.saveFromTree(tree, "styled-board.spb");
| Format | Background | Font | Border | Advanced |
|---|---|---|---|---|
| Snap/SPS | β Full | β Full | β Full | β All properties |
| TouchChat | β Full | β Full | β Full | β Label position, transparency |
| OBF/OBZ | β Yes | β No | β Yes | β Basic only |
| Grid3 | β Yes | β Yes | β Yes | β Style references |
| Asterics Grid | β Yes | β Yes | β Yes | β Metadata-based |
| Apple Panels | β Yes | β Size only | β No | β Display weight |
| Dot | βNo | β Yes | β No | β Basic only |
| OPML | βNo | β Yes | β No | β Basic only |
| Excel | β Yes | β Size only | β No | β Display weight |
import { getProcessor } from "aac-processors";
// Load styled content from TouchChat
const touchChatProcessor = getProcessor("input.ce");
const styledTree = touchChatProcessor.loadIntoTree("input.ce");
// Convert to Snap format while preserving styling
const snapProcessor = getProcessor("output.spb");
snapProcessor.saveFromTree(styledTree, "output.spb");
// Styling information is automatically mapped between formats
console.log("Styling preserved across formats!");
The CLI provides three main commands for working with AAC files:
# Extract all text from an AAC file
npx aac-processors extract examples/example.dot
# With format specification and verbose output
npx aac-processors extract examples/example.sps --format snap --verbose
# Convert from one format to another (format auto-detected from input extension)
npx aac-processors convert input.sps output.obf --format obf
# Convert TouchChat to Snap format
npx aac-processors convert communication.ce backup.spb --format snap
# Convert any AAC format to Excel for vocabulary analysis
npx aac-processors convert input.gridset vocabulary-analysis.xlsx --format xlsx
# Convert with button filtering options
npx aac-processors convert input.gridset output.grd --format grd --preserve-all-buttons
npx aac-processors convert input.ce output.spb --format snap --exclude-buttons "settings,menu"
npx aac-processors convert input.obf output.gridset --format gridset --no-exclude-system
# Get detailed file information in JSON format
npx aac-processors analyze examples/example.ce
# Get human-readable file information
npx aac-processors analyze examples/example.gridset --pretty
General Options:
--format <format> - Specify format type (auto-detected if not provided)--pretty - Human-readable output (analyze command)--verbose - Detailed output (extract command)--quiet - Minimal output (extract command)--gridset-password <password> - Password for encrypted Grid 3 archives (.gridsetx)Button Filtering Options:
--preserve-all-buttons - Preserve all buttons including navigation/system buttons--no-exclude-navigation - Don't exclude navigation buttons (Home, Back)--no-exclude-system - Don't exclude system buttons (Delete, Clear, etc.)--exclude-buttons <list> - Comma-separated list of button labels/terms to excludeExamples:
# Extract text with all buttons preserved
npx aac-processors extract input.ce --preserve-all-buttons --verbose
# Convert excluding only custom buttons
npx aac-processors convert input.gridset output.grd --format grd --exclude-buttons "settings,help,menu"
# Analyze with navigation buttons excluded but system buttons preserved
npx aac-processors analyze input.spb --no-exclude-system --pretty
getProcessor(filePathOrExtension: string): BaseProcessorFactory function that returns the appropriate processor for a file extension.
const processor = getProcessor(".dot"); // Returns DotProcessor
const processor2 = getProcessor("file.obf"); // Returns ObfProcessor
BaseProcessorAbstract base class for all processors with these key methods:
loadIntoTree(filePathOrBuffer: string | Buffer): AACTree - Load file into tree structuresaveFromTree(tree: AACTree, outputPath: string): void - Save tree to fileextractTexts(filePathOrBuffer: string | Buffer): string[] - Extract all text contentprocessTexts(input: string | Buffer, translations: Map<string, string>, outputPath: string): Buffer - Apply translationsAACTreeCore data structure representing a communication board:
interface AACTree {
pages: Record<string, AACPage>;
rootId?: string;
addPage(page: AACPage): void;
traverse(callback: (page: AACPage) => void): void;
}
AACPageRepresents a single page/screen in a communication board:
interface AACPage {
id: string;
name: string;
buttons: AACButton[];
parentId?: string;
addButton(button: AACButton): void;
}
AACButtonRepresents a button/cell in a communication board:
interface AACButton {
id: string;
label: string;
message?: string;
type: "SPEAK" | "NAVIGATE";
targetPageId?: string; // For navigation buttons
}
| Processor | File Extensions | Description |
|---|---|---|
DotProcessor |
.dot |
Graphviz DOT format |
OpmlProcessor |
.opml |
OPML hierarchical format |
ObfProcessor |
.obf, .obz |
Open Board Format (JSON/ZIP) |
SnapProcessor |
.sps, .spb |
Tobii Dynavox Snap format |
GridsetProcessor |
.gridset, .gridsetx |
Smartbox Grid 3 format |
TouchChatProcessor |
.ce |
PRC-Saltillo TouchChat format |
ApplePanelsProcessor |
.plist |
iOS Apple Panels format |
AstericsGridProcessor |
.grd |
Asterics Grid native format |
ExcelProcessor |
.xlsx |
Microsoft Excel format |
This library maintains 65% test coverage with 111 comprehensive tests including:
# Run all tests (automatically builds first)
npm test
# Run with coverage report (automatically builds first)
npm run test:coverage
# Run tests in watch mode (automatically builds first)
npm run test:watch
# Generate detailed coverage analysis
npm run coverage:report
Note: All test commands automatically run npm run build first to ensure CLI tests have the required dist/ files. CLI integration tests require the compiled JavaScript files to test the command-line interface.
A wide range of utility scripts for batch processing, audio generation, and advanced analysis are available in the scripts/ directory. These include:
# Build TypeScript
npm run build
# Watch mode for development
npm run build:watch
# Lint code
npm run lint
# Format code
npm run format
# Type checking
npm run type-check
We welcome contributions! Please read our Contributor License Agreement (CLA) before you get started.
git checkout -b feature/amazing-featurenpm testgit commit -m 'Add amazing feature'git push origin feature/amazing-featuregit clone https://github.com/willwade/AACProcessors-nodejs.git
cd AACProcessors-nodejs
npm install
npm run build
npm test
cp .envrc.example .envrc.envrc is ignored to prevent accidental commits.envrcβnever commit real secretspackage.json at 0.0.0-development; release tags control the published version.v2.1.0). Publishing only runs for non-prerelease tags..github/workflows/publish.yml) automatically installs dependencies, rewrites the package version from the tag, and runs the standard publish pipeline.MIT License - see LICENSE file for details.
Created by Will Wade and contributors.
Inspired by the Python AACProcessors project
SnapProcessor save/load cycle (5 failing tests remaining).AACTree level.Want to help with any of these items? See our Contributing Guidelines and pick an issue that interests you!