npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@unisat/animated-qr

v1.0.3

Published

Animated QR code library with BBQR protocol support

Readme

@unisat/animated-qr

🔗 Multi-part animated QR code library for large data transmission with BBQR protocol support

npm version License: MIT

📋 Quick Overview

What it does: Splits large data (Bitcoin transactions, account data) into multiple animated QR codes for reliable mobile transmission.

Core Protocol: BBQR (Bitcoin QR) - Custom format with compression and multi-part support.

Use Cases: Bitcoin wallet data sync, PSBT transmission, large transaction signing.

🚀 Core Features

  • Multi-part QR splitting - Handles data up to ~500KB
  • BBQR protocol - Efficient compression + error detection
  • React components - Ready-to-use UI components
  • Progressive scanning - Scan parts in any order
  • TypeScript native - Full type safety
  • Zero external deps - Only uses lz-string + qrcode

📦 Installation

npm install @unisat/animated-qr

# For React components
npm install react qrcode.react

⚡ Quick Start

Basic Encode/Decode

import { encode, decode } from '@unisat/animated-qr';

// Encode large data → QR code array
const qrParts = encode('very long bitcoin data...', 'psbt');
// → ['B$HP010150hex...', 'B$HP020250hex...', ...]

// Decode QR array → original data  
const originalData = decode(qrParts);

Progressive Scanning

import { createDecoder } from '@unisat/animated-qr';

const decoder = createDecoder();

// Scan QR codes one by one
const result = decoder.addPart('B$HP010150hex...');
console.log(`Progress: ${result.progress.percentage}%`);

if (result.isComplete) {
  console.log('Data:', result.data);
}

React Component

import { AnimatedQR } from '@unisat/animated-qr/react';

<AnimatedQR
  type="psbt"
  data={psbtHexString}
  options={{
    size: 300,
    autoPlay: true,
    showProgress: true,
    maxChunkSize: 800
  }}
/>

📚 API Reference

Core Functions

| Function | Parameters | Returns | Description | |----------|------------|---------|-------------| | encode(data, type?, options?) | data: stringtype: DataTypeoptions: QROptions | string[] | Split data into QR parts | | decode(qrParts) | qrParts: string[] | string | Reconstruct original data | | createDecoder() | - | BBQRDecoder | Create progressive decoder | | createEncoder() | - | BBQREncoder | Create encoder instance | | detectProtocol(qrText) | qrText: string | 'bbqr' \| 'bc-ur' \| null | Detect QR protocol | | isSupportedFormat(qrText) | qrText: string | boolean | Check if QR is supported |

Data Types

// Data categories
type DataType = 'psbt' | 'transaction' | 'account' | 'signature';

// Protocol types  
type Protocol = 'bbqr' | 'bc-ur';

// Encoding options
interface QROptions {
  maxChunkSize?: number;    // Default: 800
  compression?: boolean;    // Default: true
  protocolOptions?: Record<string, any>;
}

// Scan progress tracking
interface ScanProgress {
  collected: number;        // Parts received
  total: number;           // Total parts needed
  percentage: number;      // Completion %
  missing?: number[];      // Missing part indices
}

// Scan result
interface ScanResult {
  isComplete: boolean;
  data?: string;           // Available when complete
  dataType?: DataType;
  protocol?: Protocol;
  progress: ScanProgress;
  metadata?: Record<string, any>;
}

React Components

<AnimatedQR>

interface AnimatedQRProps {
  // Data input (choose one)
  data?: string;           // Raw data to encode
  qrParts?: string[];      // Pre-encoded QR parts

  // Configuration
  type?: DataType;         // Default: 'account'
  protocol?: Protocol;     // Default: 'bbqr'
  
  // Display options
  options?: {
    size?: number;         // QR size in pixels (default: 300)
    interval?: number;     // Animation interval ms (default: 300)
    autoPlay?: boolean;    // Auto-advance parts (default: true)
    showProgress?: boolean;// Show dots indicator (default: true)
    showControls?: boolean;// Show prev/next buttons (default: true)
    showProtocolInfo?: boolean; // Show protocol info (default: false)
    
    // Encoding options
    maxChunkSize?: number; // Max chars per QR (default: 800)
    compression?: boolean; // Enable compression (default: true)
    errorCorrectionLevel?: 'L' | 'M' | 'Q' | 'H'; // Default: 'M'
  };
  
  // Styling
  style?: React.CSSProperties;
  className?: string;
}

BBQRDecoder Methods

class BBQRDecoder {
  addPart(qrText: string): ScanResult;
  reset(): void;
  getProgress(): ScanProgress;
  getProtocolType(): Protocol;
}

BBQREncoder Methods

class BBQREncoder {
  encode(data: string, dataType: DataType, options?: QROptions): string[];
  getProtocolType(): Protocol;
}

🎯 Usage Examples

1. Bitcoin PSBT Transmission

import { encode, createDecoder } from '@unisat/animated-qr';

// Sender side - encode PSBT
const psbtHex = "70736274ff01007d..."; // Your PSBT hex
const qrParts = encode(psbtHex, 'psbt', { maxChunkSize: 1000 });

console.log(`Split into ${qrParts.length} QR codes`);

// Receiver side - decode step by step
const decoder = createDecoder();
for (const part of qrParts) {
  const result = decoder.addPart(part);
  console.log(`Progress: ${result.progress.percentage}%`);
  
  if (result.isComplete) {
    console.log('Received PSBT:', result.data);
    break;
  }
}

2. Account Sync Data

// Prepare account data
const accountData = JSON.stringify({
  publicKeys: ['xpub6D3i46Y43SFfjEBYheBK3btYMRm9Cfb8Tt4M5Bv16tArNBw5ATNyJWjdcMyLxoCdHWTvm3ak7j2BWacq5Lw478aYUeARoYm4dvaQgJBAGsb'],
  addresses: ['bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh'],
  addressType: 0
});

const qrParts = encode(accountData, 'account', {
  compression: true,
  maxChunkSize: 600
});

3. React Component with Custom Styling

import { AnimatedQR } from '@unisat/animated-qr/react';

function WalletQRDisplay({ transactionData }) {
  return (
    <div className="qr-container">
      <h3>Scan to Import Transaction</h3>
      <AnimatedQR
        type="transaction"
        data={transactionData}
        options={{
          size: 400,
          autoPlay: true,
          interval: 500,
          showProgress: true,
          showControls: true,
          showProtocolInfo: true,
          maxChunkSize: 1200,
          compression: true
        }}
        style={{
          border: '2px solid #007bff',
          borderRadius: '10px',
          padding: '20px'
        }}
      />
    </div>
  );
}

4. Error Handling & Protocol Detection

import { detectProtocol, isSupportedFormat, createDecoder } from '@unisat/animated-qr';

function handleQRScan(qrText: string) {
  // Check if QR is supported
  if (!isSupportedFormat(qrText)) {
    console.error('Unsupported QR format');
    return;
  }

  // Detect protocol
  const protocol = detectProtocol(qrText);
  console.log('Detected protocol:', protocol);

  // Process based on protocol
  if (protocol === 'bbqr') {
    const decoder = createDecoder();
    try {
      const result = decoder.addPart(qrText);
      
      if (result.isComplete && result.data) {
        handleCompleteData(result.data, result.dataType);
      } else {
        updateScanProgress(result.progress);
      }
    } catch (error) {
      console.error('Decode error:', error.message);
    }
  }
}

function handleCompleteData(data: string, dataType?: DataType) {
  switch (dataType) {
    case 'psbt':
      console.log('Received PSBT:', data);
      break;
    case 'transaction':
      console.log('Received transaction:', data);
      break;
    case 'account':
      console.log('Received account data:', data);
      break;
    default:
      console.log('Received data:', data);
  }
}

5. Large Data Optimization

// For very large data, optimize chunk size
const largeAccountData = JSON.stringify(hugeAccountObject);

// Test different chunk sizes
const sizes = [600, 800, 1000, 1200];
sizes.forEach(size => {
  const parts = encode(largeAccountData, 'account', { maxChunkSize: size });
  console.log(`Chunk size ${size}: ${parts.length} QR codes`);
});

// Use optimal size (fewer QR codes)
const optimizedParts = encode(largeAccountData, 'account', {
  maxChunkSize: 1200,
  compression: true
});

🔧 BBQR Protocol Specification

Format Structure

B$[encoding][fileType][totalParts][currentPart][data]

Components:

  • B$: Protocol identifier (required)
  • encoding: H (Hex) | J (JSON) | 2 (Base32)
  • fileType: P (PSBT) | T (Transaction) | J (JSON) | A (Account)
  • totalParts: Base36 number (1-1295 parts supported)
  • currentPart: Base36 number (0-indexed)
  • data: Encoded payload

Examples:

B$HP0100cHNidP8BAH...           # Single PSBT part
B$JP020150{"psbt":"cH..."}      # JSON part 1 of 2
B$HP020250736274ff01007d...     # Hex part 2 of 2

Data Type Mapping

| DataType | BBQR Code | Description | |----------|-----------|-------------| | psbt | P | Bitcoin PSBT (hex encoded) | | transaction | T | Signed transaction (hex) | | account | J | Account data (JSON) | | signature | J | Signature result (JSON) |

Compression

Uses lz-string compression when enabled:

// Compression reduces QR count for repetitive data
const uncompressed = encode(data, 'account', { compression: false });
const compressed = encode(data, 'account', { compression: true });

console.log(`Uncompressed: ${uncompressed.length} parts`);
console.log(`Compressed: ${compressed.length} parts`);

🛠️ Development Setup

# Clone and install
git clone https://github.com/unisat-wallet/animated-qr.git
cd animated-qr
npm install

# Development commands
npm run dev        # Watch mode
npm run build      # Production build
npm run test       # Run tests
npm run lint       # Code linting

Project Structure

src/
├── core/
│   └── bbqr.ts           # BBQR protocol implementation
├── react/
│   ├── AnimatedQR.tsx    # Main React component
│   └── index.ts          # React exports
├── types/
│   ├── protocols.ts      # Core type definitions
│   └── react.ts          # React component types
└── index.ts              # Main library exports

examples/
├── basic/                # Vanilla JS example
└── react/                # React example

📝 Integration Checklist

When integrating into your project:

  • [ ] Install package: npm install @unisat/animated-qr
  • [ ] For React: Install react and qrcode.react peer dependencies
  • [ ] Import core functions or React components
  • [ ] Choose appropriate DataType for your use case
  • [ ] Set maxChunkSize based on your QR scanner capabilities
  • [ ] Enable compression for large data
  • [ ] Handle scan progress and errors appropriately
  • [ ] Test with real QR scanner apps

🔗 Links & Resources

📄 License

MIT License - Free for commercial and personal use.


For AI Integration: This library provides a complete solution for QR-based data transmission in Bitcoin/blockchain applications. Key integration points are the encode(), decode(), and <AnimatedQR> React component. All functions are type-safe and well-documented.