This document provides practical examples of using captcha-canvas in various real-world scenarios.
const { createCaptcha } = require("captcha-canvas");
const fs = require("fs");
async function generateSimpleCaptcha() {
    const { image, text } = createCaptcha(300, 100);
    const buffer = await image;
    
    fs.writeFileSync("captcha.png", buffer);
    console.log("CAPTCHA text:", text);
}
generateSimpleCaptcha();
const { CaptchaGenerator } = require("captcha-canvas");
const fs = require("fs");
async function generateAdvancedCaptcha() {
    const captcha = new CaptchaGenerator()
        .setDimension(400, 150)
        .setCaptcha({
            text: "SECURE",
            size: 60,
            color: "#2c3e50"
        })
        .setTrace({
            color: "#95a5a6",
            size: 4
        });
    
    const buffer = await captcha.generate();
    fs.writeFileSync("advanced-captcha.png", buffer);
    console.log("CAPTCHA text:", captcha.text);
}
generateAdvancedCaptcha();
import { CaptchaGenerator, createCaptcha, SetCaptchaOption } from 'captcha-canvas';
import fs from 'fs';
async function typescriptExample(): Promise<void> {
    // Using the simple function API
    const { image, text } = createCaptcha(300, 100, {
        captcha: { characters: 6, size: 40 },
        trace: { color: '#95a5a6' }
    });
    
    const buffer = await image;
    fs.writeFileSync('simple-captcha.png', buffer);
    
    // Using the class API with type safety
    const config: SetCaptchaOption = {
        text: 'HELLO',
        size: 50,
        colors: ['#e74c3c', '#3498db']
    };
    
    const captcha = new CaptchaGenerator({ width: 350, height: 120 })
        .setCaptcha(config);
    
    const classBuffer = await captcha.generate();
    fs.writeFileSync('class-captcha.png', classBuffer);
    
    console.log('Generated text:', captcha.text);
}
const express = require("express");
const session = require("express-session");
const { createCaptcha } = require("captcha-canvas");
const app = express();
app.use(session({
    secret: "your-secret-key",
    resave: false,
    saveUninitialized: true,
    cookie: { maxAge: 300000 } // 5 minutes
}));
app.use(express.json());
app.use(express.static('public'));
// Generate CAPTCHA endpoint
app.get("/captcha", async (req, res) => {
    try {
        const { image, text } = createCaptcha(300, 100, {
            captcha: { 
                characters: 6,
                size: 40,
                rotate: 15
            },
            trace: { opacity: 0.8, size: 3 },
            decoy: { total: 25, opacity: 0.3 }
        });
        
        // Store solution in session with timestamp
        req.session.captcha = {
            solution: text,
            timestamp: Date.now()
        };
        
        const buffer = await image;
        res.type("png").send(buffer);
    } catch (error) {
        console.error('CAPTCHA generation error:', error);
        res.status(500).json({ error: 'Failed to generate CAPTCHA' });
    }
});
// Verify CAPTCHA endpoint
app.post("/verify", (req, res) => {
    const { captcha } = req.body;
    const sessionData = req.session.captcha;
    
    // Check if CAPTCHA exists and hasn't expired (5 minutes)
    if (!sessionData || Date.now() - sessionData.timestamp > 300000) {
        return res.json({ 
            success: false, 
            message: "CAPTCHA expired. Please refresh." 
        });
    }
    
    if (captcha && captcha.toUpperCase() === sessionData.solution) {
        res.json({ success: true, message: "CAPTCHA verified!" });
    } else {
        res.json({ success: false, message: "Invalid CAPTCHA" });
    }
    
    // Clear session data
    delete req.session.captcha;
});
// Refresh CAPTCHA endpoint
app.post("/captcha/refresh", async (req, res) => {
    try {
        const { image, text } = createCaptcha(300, 100);
        
        req.session.captcha = {
            solution: text,
            timestamp: Date.now()
        };
        
        const buffer = await image;
        res.type("png").send(buffer);
    } catch (error) {
        res.status(500).json({ error: 'Failed to refresh CAPTCHA' });
    }
});
app.listen(3000, () => {
    console.log("Server running on port 3000");
});
// pages/api/captcha.js
import { createCaptcha } from 'captcha-canvas';
import { withIronSession } from 'next-iron-session';
async function handler(req, res) {
    if (req.method === 'GET') {
        try {
            const { image, text } = createCaptcha(300, 100, {
                captcha: { characters: 6 },
                decoy: { total: 30, opacity: 0.3 }
            });
            
            req.session.set('captcha', {
                solution: text,
                timestamp: Date.now()
            });
            await req.session.save();
            
            const buffer = await image;
            res.setHeader('Content-Type', 'image/png');
            res.send(buffer);
        } catch (error) {
            res.status(500).json({ error: 'CAPTCHA generation failed' });
        }
    } else if (req.method === 'POST') {
        const { captcha } = req.body;
        const sessionData = req.session.get('captcha');
        
        if (!sessionData || Date.now() - sessionData.timestamp > 300000) {
            return res.json({ success: false, message: 'CAPTCHA expired' });
        }
        
        const isValid = captcha?.toUpperCase() === sessionData.solution;
        
        if (isValid) {
            req.session.destroy();
        }
        
        res.json({ 
            success: isValid, 
            message: isValid ? 'Verified' : 'Invalid CAPTCHA' 
        });
    }
}
export default withIronSession(handler, {
    password: process.env.SECRET_COOKIE_PASSWORD,
    cookieName: 'captcha-session'
});
const { CaptchaGenerator } = require("captcha-canvas");
async function rainbowCaptcha() {
    const captcha = new CaptchaGenerator()
        .setDimension(450, 150)
        .setCaptcha({
            text: "RAINBOW",
            size: 55,
            colors: [
                "#e74c3c", // Red
                "#f39c12", // Orange  
                "#f1c40f", // Yellow
                "#27ae60", // Green
                "#3498db", // Blue
                "#9b59b6", // Purple
                "#e91e63"  // Pink
            ],
            rotate: 10,
            skew: true
        })
        .setTrace({ 
            color: "#34495e", 
            size: 3, 
            opacity: 0.7 
        });
    
    return {
        buffer: await captcha.generate(),
        text: captcha.text
    };
}
const { CaptchaGenerator } = require("captcha-canvas");
async function multiStyledCaptcha() {
    const captcha = new CaptchaGenerator({ width: 500, height: 150 })
        .setCaptcha([
            { 
                text: "SEC", 
                size: 60, 
                color: "#e74c3c", 
                font: "Arial",
                rotate: 15 
            },
            { 
                text: "URE", 
                size: 50, 
                color: "#27ae60", 
                font: "Times",
                skew: true 
            },
            { 
                text: "123", 
                size: 45, 
                color: "#3498db", 
                font: "Courier",
                rotate: -10 
            }
        ])
        .setTrace({ color: "#95a5a6", size: 4 });
    
    return await captcha.generate();
}
const { CaptchaGenerator, resolveImage } = require("captcha-canvas");
async function noisyBackgroundCaptcha() {
    // Create or load a noise pattern background
    const background = await resolveImage("./assets/noise-pattern.jpg");
    
    const captcha = new CaptchaGenerator()
        .setDimension(400, 150)
        .setBackground(background)
        .setCaptcha({
            text: "SECURE",
            size: 60,
            opacity: 0.95,  // High opacity to stand out
            colors: ["#ffffff", "#f8f9fa"], // Light colors for dark background
            rotate: 12,
            skew: true
        })
        .setTrace({ 
            color: "#ecf0f1", 
            size: 3, 
            opacity: 0.8 
        })
        .setDecoy({ 
            total: 40, 
            opacity: 0.2, 
            color: "#bdc3c7" 
        });
    
    return await captcha.generate();
}
const { CaptchaGenerator } = require("captcha-canvas");
async function fortKnoxCaptcha() {
    const captcha = new CaptchaGenerator({ width: 500, height: 200 })
        .setCaptcha({
            characters: 8,  // Longer text
            size: 55,
            rotate: 25,     // More rotation
            skew: true,
            colors: [
                "#2c3e50", "#e74c3c", "#27ae60", 
                "#f39c12", "#8e44ad", "#16a085"
            ],
            opacity: 0.9
        })
        .setTrace({
            size: 6,        // Thicker trace lines
            opacity: 0.9,
            color: "#34495e"
        })
        .setDecoy({
            total: 80,      // Many decoys
            opacity: 0.4,
            size: 25,
            color: "#7f8c8d"
        });
    
    return {
        buffer: await captcha.generate(),
        solution: captcha.text,
        difficulty: "maximum"
    };
}
const { createCaptchaSync } = require("captcha-canvas");
const fs = require("fs");
const path = require("path");
function generateCaptchaBatch(count, outputDir = "./captchas") {
    // Ensure output directory exists
    if (!fs.existsSync(outputDir)) {
        fs.mkdirSync(outputDir, { recursive: true });
    }
    
    const captchas = [];
    const startTime = Date.now();
    
    for (let i = 0; i < count; i++) {
        const { image, text } = createCaptchaSync(300, 100, {
            captcha: { 
                characters: 6,
                size: 40,
                rotate: 15
            },
            decoy: { total: 25, opacity: 0.3 },
            trace: { size: 3, opacity: 0.8 }
        });
        
        const filename = `captcha-${Date.now()}-${i}.png`;
        const filepath = path.join(outputDir, filename);
        
        fs.writeFileSync(filepath, image);
        
        captchas.push({
            id: i + 1,
            filename,
            filepath,
            solution: text,
            timestamp: Date.now()
        });
        
        // Progress logging
        if ((i + 1) % 10 === 0) {
            console.log(`Generated ${i + 1}/${count} CAPTCHAs`);
        }
    }
    
    const duration = Date.now() - startTime;
    console.log(`Batch complete: ${count} CAPTCHAs in ${duration}ms`);
    
    // Save metadata
    const metadata = {
        count,
        duration,
        averageTime: duration / count,
        captchas
    };
    
    fs.writeFileSync(
        path.join(outputDir, 'metadata.json'), 
        JSON.stringify(metadata, null, 2)
    );
    
    return metadata;
}
// Generate 100 CAPTCHAs
const batch = generateCaptchaBatch(100);
console.log(`Average generation time: ${batch.averageTime.toFixed(2)}ms`);
const { CaptchaGenerator, createCaptchaSync } = require("captcha-canvas");
class CaptchaService {
    constructor() {
        this.fallbackEnabled = true;
        this.retryAttempts = 3;
    }
    
    async generateRobustCaptcha(config = {}) {
        const attempts = [];
        
        // Primary generation attempt
        for (let attempt = 1; attempt <= this.retryAttempts; attempt++) {
            try {
                const captcha = new CaptchaGenerator(config.dimensions)
                    .setCaptcha(config.captcha || { characters: 6 })
                    .setTrace(config.trace || {})
                    .setDecoy(config.decoy || {});
                
                if (config.background) {
                    captcha.setBackground(config.background);
                }
                
                const buffer = await captcha.generate();
                
                return {
                    success: true,
                    buffer,
                    text: captcha.text,
                    method: 'primary',
                    attempt
                };
            } catch (error) {
                attempts.push({
                    attempt,
                    error: error.message,
                    timestamp: new Date().toISOString()
                });
                
                console.warn(`Attempt ${attempt} failed:`, error.message);
                
                // Wait before retry (exponential backoff)
                if (attempt < this.retryAttempts) {
                    await new Promise(resolve => 
                        setTimeout(resolve, Math.pow(2, attempt) * 100)
                    );
                }
            }
        }
        
        // Fallback to synchronous generation
        if (this.fallbackEnabled) {
            try {
                console.log('Falling back to synchronous generation');
                
                const { image, text } = createCaptchaSync(
                    config.dimensions?.width || 300,
                    config.dimensions?.height || 100,
                    {
                        captcha: config.captcha || { characters: 6 },
                        trace: config.trace || {},
                        decoy: config.decoy || {}
                    }
                );
                
                return {
                    success: true,
                    buffer: image,
                    text,
                    method: 'fallback',
                    attempts
                };
            } catch (fallbackError) {
                attempts.push({
                    method: 'fallback',
                    error: fallbackError.message,
                    timestamp: new Date().toISOString()
                });
            }
        }
        
        // Complete failure
        return {
            success: false,
            error: 'All generation attempts failed',
            attempts
        };
    }
}
// Usage
const service = new CaptchaService();
async function handleCaptchaRequest(req, res) {
    try {
        const result = await service.generateRobustCaptcha({
            dimensions: { width: 300, height: 100 },
            captcha: { characters: 6, size: 40 },
            trace: { size: 3 },
            decoy: { total: 25, opacity: 0.3 }
        });
        
        if (result.success) {
            req.session.captcha = result.text;
            res.type('png').send(result.buffer);
        } else {
            console.error('CAPTCHA generation failed:', result);
            res.status(500).json({ 
                error: 'CAPTCHA generation failed',
                details: result.attempts 
            });
        }
    } catch (error) {
        console.error('CAPTCHA request error:', error);
        res.status(500).json({ error: 'Internal server error' });
    }
}
const { createCaptcha, createCaptchaSync, CaptchaGenerator } = require("captcha-canvas");
describe("CAPTCHA Generation Tests", () => {
    describe("Basic Functionality", () => {
        test("should generate CAPTCHA with correct format", async () => {
            const { image, text } = createCaptcha(300, 100);
            const buffer = await image;
            
            expect(buffer).toBeInstanceOf(Buffer);
            expect(buffer.length).toBeGreaterThan(0);
            expect(text).toMatch(/^[A-F0-9]+$/);
            expect(text.length).toBeGreaterThan(0);
        });
        
        test("should generate synchronous CAPTCHA", () => {
            const { image, text } = createCaptchaSync(300, 100);
            
            expect(image).toBeInstanceOf(Buffer);
            expect(text).toMatch(/^[A-F0-9]+$/);
        });
        
        test("should respect custom dimensions", async () => {
            const { image } = createCaptcha(400, 200);
            const buffer = await image;
            
            // PNG signature check
            expect(buffer.slice(0, 8)).toEqual(
                Buffer.from([137, 80, 78, 71, 13, 10, 26, 10])
            );
        });
    });
    
    describe("CaptchaGenerator Class", () => {
        test("should create instance with default dimensions", () => {
            const captcha = new CaptchaGenerator();
            expect(captcha).toBeInstanceOf(CaptchaGenerator);
        });
        
        test("should handle method chaining", async () => {
            const captcha = new CaptchaGenerator()
                .setDimension(350, 120)
                .setCaptcha({ text: "TEST123" })
                .setTrace({ color: "#ff0000" });
            
            const buffer = await captcha.generate();
            expect(buffer).toBeInstanceOf(Buffer);
            expect(captcha.text).toBe("TEST123");
        });
        
        test("should generate random text when characters specified", async () => {
            const captcha = new CaptchaGenerator()
                .setCaptcha({ characters: 8 });
            
            await captcha.generate();
            expect(captcha.text).toHaveLength(8);
            expect(captcha.text).toMatch(/^[A-F0-9]+$/);
        });
        
        test("should handle multi-styled text arrays", async () => {
            const captcha = new CaptchaGenerator()
                .setCaptcha([
                    { text: "HE", color: "#ff0000" },
                    { text: "LLO", color: "#00ff00" }
                ]);
            
            await captcha.generate();
            expect(captcha.text).toBe("HELLO");
        });
    });
    
    describe("Configuration Validation", () => {
        test("should handle invalid dimensions gracefully", () => {
            expect(() => {
                new CaptchaGenerator({ width: -100, height: -50 });
            }).not.toThrow();
        });
        
        test("should throw error for array captcha without text", () => {
            expect(() => {
                new CaptchaGenerator().setCaptcha([
                    { size: 40 }, // Missing text property
                    { text: "WORLD" }
                ]);
            }).toThrow("Each captcha option in array must have a text property.");
        });
    });
    
    describe("Performance Tests", () => {
        test("should generate CAPTCHA within reasonable time", async () => {
            const startTime = Date.now();
            const { image } = createCaptcha(300, 100);
            await image;
            const duration = Date.now() - startTime;
            
            expect(duration).toBeLessThan(1000); // Should complete within 1 second
        });
        
        test("should handle batch generation efficiently", () => {
            const startTime = Date.now();
            const results = [];
            
            for (let i = 0; i < 10; i++) {
                const { image, text } = createCaptchaSync(300, 100);
                results.push({ image, text });
            }
            
            const duration = Date.now() - startTime;
            expect(results).toHaveLength(10);
            expect(duration).toBeLessThan(2000); // 10 CAPTCHAs in under 2 seconds
        });
    });
});
const { CaptchaGenerator, resolveImage } = require("captcha-canvas");
const LRU = require("lru-cache");
class OptimizedCaptchaService {
    constructor(options = {}) {
        this.backgroundCache = new LRU({
            max: options.maxBackgrounds || 10,
            ttl: options.backgroundTTL || 1000 * 60 * 60 // 1 hour
        });
        
        this.generatorPool = [];
        this.poolSize = options.poolSize || 5;
        
        // Pre-create generator instances
        for (let i = 0; i < this.poolSize; i++) {
            this.generatorPool.push(new CaptchaGenerator());
        }
        
        this.currentGenerator = 0;
    }
    
    async loadBackground(path) {
        if (!this.backgroundCache.has(path)) {
            try {
                const image = await resolveImage(path);
                this.backgroundCache.set(path, image);
            } catch (error) {
                console.error(`Failed to load background: ${path}`, error);
                return null;
            }
        }
        return this.backgroundCache.get(path);
    }
    
    getGenerator() {
        const generator = this.generatorPool[this.currentGenerator];
        this.currentGenerator = (this.currentGenerator + 1) % this.poolSize;
        return generator;
    }
    
    async generateOptimized(config = {}) {
        const generator = this.getGenerator();
        
        // Reset generator state
        generator
            .setDimension(config.width || 300, config.height || 100)
            .setCaptcha(config.captcha || { characters: 6 })
            .setTrace(config.trace || { size: 3, opacity: 0.8 })
            .setDecoy(config.decoy || { total: 25, opacity: 0.3 });
        
        // Handle background if specified
        if (config.backgroundPath) {
            const background = await this.loadBackground(config.backgroundPath);
            if (background) {
                generator.setBackground(background);
            }
        }
        
        return {
            buffer: await generator.generate(),
            text: generator.text
        };
    }
}
// Usage
const service = new OptimizedCaptchaService({
    poolSize: 3,
    maxBackgrounds: 5
});
const { CaptchaGenerator } = require("captcha-canvas");
class ResponsiveCaptchaService {
    constructor() {
        this.presets = {
            mobile: {
                dimensions: { width: 250, height: 80 },
                captcha: { 
                    characters: 5, 
                    size: 28, 
                    rotate: 8,
                    skew: false  // Less distortion for mobile
                },
                trace: { size: 2, opacity: 0.6 },
                decoy: { total: 15, opacity: 0.2 }
            },
            tablet: {
                dimensions: { width: 320, height: 100 },
                captcha: { 
                    characters: 6, 
                    size: 35, 
                    rotate: 12 
                },
                trace: { size: 2.5, opacity: 0.7 },
                decoy: { total: 20, opacity: 0.25 }
            },
            desktop: {
                dimensions: { width: 400, height: 120 },
                captcha: { 
                    characters: 6, 
                    size: 45, 
                    rotate: 15 
                },
                trace: { size: 3, opacity: 0.8 },
                decoy: { total: 30, opacity: 0.3 }
            },
            accessibility: {
                dimensions: { width: 450, height: 150 },
                captcha: { 
                    characters: 5, 
                    size: 60, 
                    rotate: 5,    // Minimal rotation
                    skew: false,  // No skewing
                    color: "#000000"  // High contrast
                },
                trace: { size: 2, opacity: 0.3 },
                decoy: { total: 10, opacity: 0.1 }
            }
        };
    }
    
    async generate(deviceType = 'desktop', customConfig = {}) {
        const preset = this.presets[deviceType] || this.presets.desktop;
        
        // Merge preset with custom configuration
        const config = {
            dimensions: { ...preset.dimensions, ...customConfig.dimensions },
            captcha: { ...preset.captcha, ...customConfig.captcha },
            trace: { ...preset.trace, ...customConfig.trace },
            decoy: { ...preset.decoy, ...customConfig.decoy }
        };
        
        const captcha = new CaptchaGenerator(config.dimensions)
            .setCaptcha(config.captcha)
            .setTrace(config.trace)
            .setDecoy(config.decoy);
        
        return {
            buffer: await captcha.generate(),
            text: captcha.text,
            deviceType,
            config
        };
    }
    
    // Auto-detect device type from user agent
    detectDeviceType(userAgent) {
        const ua = userAgent.toLowerCase();
        
        if (/mobile|android|iphone|ipod|blackberry|windows phone/.test(ua)) {
            return 'mobile';
        } else if (/tablet|ipad/.test(ua)) {
            return 'tablet';
        } else {
            return 'desktop';
        }
    }
    
    // Generate accessibility-friendly CAPTCHA
    async generateAccessible(options = {}) {
        return this.generate('accessibility', {
            captcha: {
                ...options,
                // Force accessibility settings
                rotate: Math.min(options.rotate || 5, 5),
                skew: false,
                color: options.color || "#000000"
            }
        });
    }
}
// Express.js integration
const responsiveService = new ResponsiveCaptchaService();
app.get('/captcha', async (req, res) => {
    try {
        const deviceType = responsiveService.detectDeviceType(
            req.headers['user-agent'] || ''
        );
        
        const accessibility = req.query.accessibility === 'true';
        
        const result = accessibility 
            ? await responsiveService.generateAccessible()
            : await responsiveService.generate(deviceType);
        
        req.session.captcha = {
            solution: result.text,
            deviceType: result.deviceType,
            timestamp: Date.now()
        };
        
        res.type('png').send(result.buffer);
    } catch (error) {
        console.error('Responsive CAPTCHA error:', error);
        res.status(500).json({ error: 'CAPTCHA generation failed' });
    }
});
This comprehensive usage guide covers all the major use cases and patterns for the captcha-canvas library, from basic generation to advanced enterprise-level implementations with error handling, performance optimization, and accessibility considerations.