@willwade/aac-processors
    Preparing search index...

    @willwade/aac-processors

    AACProcessors

    Coverage TypeScript Tests License: MIT

    A comprehensive TypeScript library for processing AAC (Augmentative and Alternative Communication) file formats with advanced translation support, cross-format conversion, and robust error handling.

    • Snap/SPS (Tobii Dynavox) - Full database support with audio
    • Grid3/Gridset (Smartbox) - XML-based format processing
    • TouchChat (PRC-Saltillo) - SQLite database handling
    • OBF/OBZ (Open Board Format) - JSON and ZIP support
    • OPML (Outline Processor Markup Language) - Hierarchical structures
    • DOT (Graphviz) - Graph-based communication boards
    • Apple Panels (MacOS) - Plist format support
    • Asterics Grid - Native Asterics Grid format with audio
    • Excel - Export to Microsoft Excel for vocabulary analysis
    • Analyics & Metrics - High-parity AAC effort metrics and clinical analysis tools
    • πŸ”„ Cross-format conversion - Convert between any supported formats
    • 🌍 Translation workflows - Built-in i18n support with processTexts()
    • 🎨 Comprehensive styling support - Preserve visual appearance across formats
    • πŸ§ͺ Property-based testing - Robust validation with 140+ tests
    • βœ… Format validation - Spec-based validation for all supported formats
    • πŸ“Š Clinical Metrics - High-parity AAC effort algorithm (v0.2) and vocabulary coverage analysis
    • ⚑ Performance optimized - Memory-efficient processing of large files
    • πŸ›‘οΈ Error recovery - Graceful handling of corrupted data
    • πŸ”’ Thread-safe - Concurrent processing support
    • πŸ“Š Comprehensive logging - Detailed operation insights

    npm install @willwade/aac-processors
    
    git clone https://github.com/willwade/AACProcessors-nodejs.git
    cd AACProcessors-nodejs
    npm install
    npm run build
    • Node.js 16.0.0 or higher
    • TypeScript 5.5+ (for development)

    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.


    • Grid 3 history: C:\Users\Public\Documents\Smartbox\Grid 3\Users\{username}\{langCode}\Phrases\history.sqlite
    • Grid 3 vocabularies: C:\Users\Public\Documents\Smartbox\Grid 3\Users\{username}\Grid Sets\
    • Snap vocabularies: 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:

    • Navigation buttons: Home, Back (toolbar navigation)
    • System buttons: Delete, Clear, Copy (text editing functions)
    • Label-based filtering: Buttons with common navigation terms
    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"),
    });
    • Cleaner conversions: Navigation bars don't clutter converted vocabularies
    • Format-appropriate: Each AAC app handles navigation/system functions in their own UI
    • Semantic-aware: Uses proper semantic action detection, not just label matching

    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).

    • Effort Scores: Calculate the physical/cognitive cost of any word (Distance, Field Size, Motor Planning).
    • Vocabulary Coverage: Compare board sets against core vocabulary lists (e.g., Anderson & Bitner).
    • Sentence Analysis: Measure the effort required to construct common test sentences.
    • Comparative Analysis: Identify gaps and improvements between two pageset versions.

    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)

      • Required fields (format, id, locale, buttons, grid, images, sounds)
      • Grid structure (rows, columns, order)
      • Button references (image_id, sound_id, load_board paths)
      • Color formats (RGB/RGBA)
      • Cross-reference validation
    • Gridset: XML structure

      • Required elements (gridset, pages, cells)
      • FixedCellSize configuration
      • Page and cell attributes
      • Image references
    • Snap: Package structure

      • ZIP package validity
      • Settings file format
      • Page/button configurations
    • TouchChat: XML structure

      • PageSet hierarchy
      • Button definitions
      • Navigation links

    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 exclude

    Examples:

    # 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

    Factory function that returns the appropriate processor for a file extension.

    const processor = getProcessor(".dot"); // Returns DotProcessor
    const processor2 = getProcessor("file.obf"); // Returns ObfProcessor

    Abstract base class for all processors with these key methods:

    • loadIntoTree(filePathOrBuffer: string | Buffer): AACTree - Load file into tree structure
    • saveFromTree(tree: AACTree, outputPath: string): void - Save tree to file
    • extractTexts(filePathOrBuffer: string | Buffer): string[] - Extract all text content
    • processTexts(input: string | Buffer, translations: Map<string, string>, outputPath: string): Buffer - Apply translations

    Core data structure representing a communication board:

    interface AACTree {
    pages: Record<string, AACPage>;
    rootId?: string;
    addPage(page: AACPage): void;
    traverse(callback: (page: AACPage) => void): void;
    }

    Represents a single page/screen in a communication board:

    interface AACPage {
    id: string;
    name: string;
    buttons: AACButton[];
    parentId?: string;
    addButton(button: AACButton): void;
    }

    Represents 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:

    • Unit tests for all processors and core functionality
    • Integration tests for cross-format workflows
    • Property-based tests using fast-check for edge case discovery
    • Performance tests for memory usage and large file handling
    • Error handling tests for corrupted data and edge cases
    # 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:

    • Analysis: Pageset reporting and vocabulary extraction.
    • Audio: Automated TTS generation and audio-enhanced pageset creation.
    • Conversion: TSV-to-Gridset and other format-shifting tools.
    • Translation: Batch translation workflows using Google, Azure, and Gemini.
    # 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.

    1. Fork the repository
    2. Create a feature branch: git checkout -b feature/amazing-feature
    3. Make your changes with tests
    4. Run the test suite: npm test
    5. Commit your changes: git commit -m 'Add amazing feature'
    6. Push to the branch: git push origin feature/amazing-feature
    7. Open a Pull Request
    git clone https://github.com/willwade/AACProcessors-nodejs.git
    cd AACProcessors-nodejs
    npm install
    npm run build
    npm test
    • Copy the template: cp .envrc.example .envrc
    • Fill in your own API keys locally; .envrc is ignored to prevent accidental commits
    • If you rotate keys, update only your local .envrcβ€”never commit real secrets
    • The repository keeps package.json at 0.0.0-development; release tags control the published version.
    • Create a GitHub release with a semantic tag (e.g. v2.1.0). Publishing only runs for non-prerelease tags.
    • The workflow (.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



    • [ ] Road Testing - Perform comprehensive layout and formatting validation across diverse pagesets to verify conversion fidelity.
    • [ ] Fix audio persistence issues - Resolve functional audio recording persistence in SnapProcessor save/load cycle (5 failing tests remaining).
    • [x] Access Method Modeling - Support for switch scanning (linear, row-column, block) integrated into AAC metrics.
    • [ ] Complete SnapProcessor coverage (currently ~60%) - Reach >75% coverage by adding comprehensive audio handling and database corruption tests.
    • [ ] Symbol System Rethink - Explore treating "Symbols" as a first-class entity (alongside Pages/Buttons) to support richer metadata (library IDs, synonyms, multi-lang names).
    • [ ] Language & Locale Persistence - Ensure current language and locale information is correctly preserved and bubbled up to the AACTree level.
    • [ ] Adaptive Metrics - Expand scanning analysis to include dwell times and more complex switch logic configurations.
    • [ ] Batch processing CLI - Process multiple files/directories in parallel.

    Want to help with any of these items? See our Contributing Guidelines and pick an issue that interests you!