Platform-agnostic library for MXW01 thermal printer with Web Bluetooth and Node.js support
npm install mxw01-thermal-printerFramework-agnostic library for MXW01 thermal printer with support for browsers, Node.js, Bun, and more.
- π― Framework-agnostic - Pure TypeScript core, works everywhere
- π Multi-platform - Browsers (Web Bluetooth), Node.js, Bun
- π¦ Zero framework dependencies - Use with React, Vue, Svelte, or vanilla JS
- π¨οΈ Advanced image processing - Multiple dithering algorithms (Floyd-Steinberg, Bayer, Atkinson, etc.)
- π Extensible adapters - Web Bluetooth, Noble (Node.js), custom adapters
- π Event-driven - Subscribe to printer events
- π Full TypeScript - Complete type safety
- π Examples included - React, Vue, Node/Bun implementations provided
``bash`
npm install mxw01-thermal-printer
For Node.js/Bun (Bluetooth):
`bash`
npm install @stoprocent/noble
For Node.js/Bun (Canvas):
`bash`
npm install canvas
For Node.js/Bun (Fabric):
`bash`
npm install fabric
`typescript
import { ThermalPrinterClient, WebBluetoothAdapter } from 'mxw01-thermal-printer';
// Create client
const adapter = new WebBluetoothAdapter();
const printer = new ThermalPrinterClient(adapter);
// Connect
await printer.connect();
// Print from canvas
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
await printer.print(imageData, {
dither: 'steinberg',
brightness: 128,
intensity: 93
});
// Disconnect
await printer.disconnect();
`
`typescript
import { ThermalPrinterClient, NodeBluetoothAdapter } from 'mxw01-thermal-printer';
import { createCanvas } from 'canvas';
// Create client
const adapter = new NodeBluetoothAdapter();
const printer = new ThermalPrinterClient(adapter);
// Connect
await printer.connect();
// Create image
const canvas = createCanvas(384, 200);
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, 384, 200);
ctx.fillStyle = 'black';
ctx.font = '30px Arial';
ctx.fillText('Hello from Node.js!', 20, 100);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
// Print
await printer.print(imageData);
// Disconnect
await printer.disconnect();
`
The library provides a framework-agnostic core that can be easily integrated with any framework. Example implementations are included in the examples/ directory:
See examples/react-hook.tsx for a complete React hook implementation.
`tsx
import { useThermalPrinter } from './examples/react-hook';
import { useRef, useEffect } from 'react';
function App() {
const canvasRef = useRef
const { isConnected, connectPrinter, printCanvas } = useThermalPrinter();
// Draw on canvas when component mounts
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext('2d');
if (!ctx) return;
// White background
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, 384, 200);
// Black text
ctx.fillStyle = 'black';
ctx.font = '30px Arial';
ctx.fillText('Hello from React!', 20, 100);
// Draw a rectangle
ctx.strokeStyle = 'black';
ctx.lineWidth = 2;
ctx.strokeRect(10, 10, 364, 180);
}, []);
const handlePrint = async () => {
if (canvasRef.current) {
await printCanvas(canvasRef.current);
}
};
return (
ref={canvasRef}
width={384}
height={200}
style={{ border: '1px solid #ccc' }}
/>
);
}
`
See examples/vue-composable.ts for a complete Vue composable implementation.
`vue
ref="canvasRef"
width="384"
height="200"
style="border: 1px solid #ccc;"
/>
`
The main client class for interacting with the printer.
`typescript
import { ThermalPrinterClient, WebBluetoothAdapter } from 'mxw01-thermal-printer';
const adapter = new WebBluetoothAdapter();
const printer = new ThermalPrinterClient(adapter);
`
#### Methods
- connect(): Promise - Connect to the printerdisconnect(): Promise
- - Disconnect from the printerprint(imageData: ImageData, options?: PrintOptions): Promise
- - Print an imagegetStatus(): Promise
- - Get printer statussetDitherMethod(method: DitherMethod): void
- - Set dithering algorithmsetPrintIntensity(intensity: number): void
- - Set print intensity (0-255)on(eventType, listener): () => void
- - Subscribe to eventsdispose(): void
- - Clean up resources
#### Properties
- isConnected: boolean - Connection statusisPrinting: boolean
- - Printing statusprinterState: PrinterState | null
- - Current printer statestatusMessage: string
- - Current status messageditherMethod: DitherMethod
- - Current dithering methodprintIntensity: number
- - Current print intensity
#### Events
`typescript
printer.on('connected', (event) => {
console.log('Connected to:', event.device.name);
});
printer.on('disconnected', () => {
console.log('Disconnected');
});
printer.on('stateChange', (event) => {
console.log('Printer state:', event.state);
});
printer.on('error', (event) => {
console.error('Error:', event.error);
});
`
`typescript`
interface PrintOptions {
dither?: DitherMethod; // Dithering algorithm
rotate?: 0 | 90 | 180 | 270; // Rotation angle
flip?: 'none' | 'h' | 'v' | 'both'; // Flip direction
brightness?: number; // Image brightness (0-255, default: 128)
intensity?: number; // Print intensity (0-255, default: 93)
}
| Method | Best For | Description |
| ------------ | --------------------- | ------------------------------ |
| threshold | Text, simple graphics | Basic black/white conversion |steinberg
| | Photos, general use | Floyd-Steinberg (recommended) |bayer
| | Patterns, textures | Ordered dithering |atkinson
| | Comics, illustrations | Atkinson dithering |pattern
| | Special effects | Pattern-based dithering |
#### brightness - Image Pre-processing
- Range: 0-255 (default: 128)
- Effect: Adjusts image lightness before printing
- Lower values (0-127): Darker image (more black pixels)
- 128: Normal (recommended)
- Higher values (129-255): Lighter image (fewer black pixels)
#### intensity - Print Head Heat
- Range: 0-255 (default: 93)
- Effect: Controls thermal print head temperature
- 50-80: Light printing
- 80-100: Normal printing (recommended)
- 100-150: Dark printing
- 150-255: Very dark (risk of paper damage)
| Use Case | brightness | intensity | Description |
| ----------- | ---------- | --------- | ------------------ |
| Normal text | 128 | 93 | Balanced, readable |
| Photos | 140 | 100 | Good contrast |
| Barcodes/QR | 128 | 110 | High contrast |
| Light draft | 150 | 70 | Saves heat |
| Dark/bold | 110 | 120 | Maximum darkness |
Complete working examples are provided in the examples/ directory:
- nodejs-canvas-example.ts - Basic Node.js/Bun implementation
- nodejs-fabric-example.ts - Node.js/Bun with Fabric.js for advanced graphics
- react-hook.tsx - React hook implementation
- vue-composable.ts - Vue 3 composable implementation
These examples show how to integrate the core library with different frameworks. You can copy and adapt them to your project.
| Platform | Support | Adapter | Notes |
| ------------ | ------- | ------------------------ | ---------------------------------- |
| Browser | β
| WebBluetoothAdapter | Requires Web Bluetooth API |
| Node.js | β
| NodeBluetoothAdapter | Requires @stoprocent/noble |
| Bun | β
| NodeBluetoothAdapter | Same as Node.js |
Web Bluetooth API is supported in:
- β
Chrome/Edge 56+
- β
Opera 43+
- β
Chrome for Android
Not supported in:
- β Firefox
- β Safari (as of 2024)
``
βββββββββββββββββββββββββββββββββββββββ
β Framework Examples β β React, Vue (examples/)
βββββββββββββββββββββββββββββββββββββββ€
β Platform-Agnostic Core β β ThermalPrinterClient (src/core/)
βββββββββββββββββββββββββββββββββββββββ€
β Bluetooth Adapters β β Web, Node.js adapters (src/adapters/)
βββββββββββββββββββββββββββββββββββββββ€
β Service Layer β β Protocol, image processing (src/services/)
βββββββββββββββββββββββββββββββββββββββ
The library is designed with clear separation of concerns:
- Core - Framework-agnostic client with event system
- Adapters - Platform-specific Bluetooth implementations
- Services - Printer protocol, image processing, dithering
- Examples - Reference implementations for different frameworks
You can create custom Bluetooth adapters for other platforms:
`typescript
import type { BluetoothAdapter } from 'mxw01-thermal-printer';
class MyCustomAdapter implements BluetoothAdapter {
async isAvailable(): Promise
// Check if Bluetooth is available
}
async requestDevice(): Promise
// Request device from user
}
async connect(device: BluetoothDevice): Promise
// Connect and return connection with characteristics
}
}
// Use your custom adapter
const printer = new ThermalPrinterClient(new MyCustomAdapter());
`
For advanced use cases, you can use the low-level protocol directly:
`typescript
import { MXW01Printer, prepareImageDataBuffer, encode1bppRow } from 'mxw01-thermal-printer';
// Create printer instance
const printer = new MXW01Printer(controlWrite, dataWrite);
// Set intensity
await printer.setIntensity(93);
// Request status
await printer.requestStatus();
// Print image
const imageBuffer = prepareImageDataBuffer(binaryRows);
await printer.printRequest(binaryRows.length, 0);
await printer.sendDataChunks(imageBuffer);
await printer.flushData();
await printer.waitForPrintComplete();
`
- Ensure Bluetooth is enabled on your device
- Make sure the printer is charged and turned on
- Try disconnecting and reconnecting
- Check that no other application is connected to the printer
- Adjust brightness and intensity settings
- Try different dithering algorithms
- Check that the thermal paper is properly loaded
- Clean the thermal print head if necessary
- Ensure @stoprocent/noble is properly installed
- On Linux, you may need to grant Bluetooth permissions
- On Windows, ensure Bluetooth drivers are up to date
- Check that no other Bluetooth service is using the adapter
The library is written in TypeScript and provides complete type definitions:
`typescript``
import type {
ThermalPrinterClient,
PrinterState,
PrintOptions,
DitherMethod,
BluetoothAdapter,
PrinterEvent
} from 'mxw01-thermal-printer';
Contributions are welcome! Please feel free to submit issues and pull requests.
MIT
Based on the MXW01 thermal printer protocol. The identification of the protocol and its operation would not have been possible without dropalltables/catprinter.
Big thank you to the original researchers and contributors.
Made with β€οΈ by ClΓ©ment Van Peuter