Skip to content

Web Best Practices

Performance Optimization

1. Choose Appropriate Processing Mode

Video Mode (ProcessMode.Video)

  • Suitable for real-time video streams and live scenarios
  • Better performance, faster processing
  • Recommended for camera preview, video calls, etc.

Image Mode (ProcessMode.Image)

  • Suitable for single image processing
  • Higher quality, better effects
  • Recommended for photo editing, image beautification, etc.
javascript
// Real-time video processing (synchronous method)
const result = engine.processImage(
    imageData, 
    imageData.width, 
    imageData.height, 
    ProcessMode.Video
);

// High-quality image processing (synchronous method)
const result = engine.processImage(
    imageData, 
    imageData.width, 
    imageData.height, 
    ProcessMode.Image
);

2. Parameter Adjustment Recommendations

Beauty Parameter Adjustment Principles

  • Start with smaller values to avoid over-beautification
  • In real-time scenarios, recommend lowering parameter values for smoothness
  • Static images can use higher parameter values
  • Adjust parameter ranges based on user groups and scenarios

Recommended Parameter Ranges

javascript
// Basic beauty parameters (real-time scenarios)
engine.setBasicParam(BasicParam.Smoothing, 0.2);  // Skin smoothing (0.0-1.0)
engine.setBasicParam(BasicParam.Whitening, 0.1);  // Whitening (0.0-1.0)
engine.setBasicParam(BasicParam.Rosiness, 0.1);   // Rosiness (0.0-1.0)

// Face reshape parameters (real-time scenarios)
engine.setReshapeParam(ReshapeParam.FaceThin, 0.1);    // Face thinning (0.0-1.0)
engine.setReshapeParam(ReshapeParam.EyeSize, 0.1);     // Eye enlargement (0.0-1.0)

// Static images can use higher parameter values
engine.setBasicParam(BasicParam.Smoothing, 0.5);
engine.setBasicParam(BasicParam.Whitening, 0.3);

3. Memory Optimization

Reuse Canvas and ImageData Objects

javascript
class ImageProcessor {
    constructor() {
        this.canvas = null;
        this.ctx = null;
        this.imageData = null;
    }
    
    initCanvas(width, height) {
        if (!this.canvas) {
            this.canvas = document.createElement('canvas');
            this.canvas.width = width;
            this.canvas.height = height;
            this.ctx = this.canvas.getContext('2d');
        } else {
            // Update Canvas if dimensions change
            if (this.canvas.width !== width || this.canvas.height !== height) {
                this.canvas.width = width;
                this.canvas.height = height;
            }
        }
    }
    
    processFrame(videoElement, engine) {
        this.initCanvas(videoElement.videoWidth, videoElement.videoHeight);
        
        // Draw video frame to Canvas
        this.ctx.drawImage(videoElement, 0, 0);
        
        // Get image data (reuse ImageData)
        this.imageData = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);
        
        // Process image (synchronous method)
        const result = engine.processImage(
            this.imageData, 
            this.canvas.width, 
            this.canvas.height, 
            ProcessMode.Video
        );
        
        // Draw result
        this.ctx.putImageData(result, 0, 0);
        
        return this.canvas;
    }
}

Release Resources Promptly

javascript
// Release resources on page unload
window.addEventListener('beforeunload', () => {
    if (engine) {
        engine.destroy();
        engine = null;
    }
});

// Vue component
onUnmounted(() => {
    if (engine) {
        engine.destroy();
        engine = null;
    }
});

// React component
useEffect(() => {
    return () => {
        if (engine) {
            engine.destroy();
            engine = null;
        }
    };
}, []);

4. Use Web Worker (Optional)

For large amounts of image processing, consider using Web Worker to avoid blocking the main thread:

javascript
// worker.js
import { BeautyEffectEngine, EngineConfig } from 'facebetter';

let engine = null;

self.onmessage = async function(e) {
    const { type, data } = e.data;
    
    switch (type) {
        case 'init':
            try {
                const config = new EngineConfig(data.config);
                engine = new BeautyEffectEngine(config);
                await engine.init();
                self.postMessage({ type: 'init', success: true });
            } catch (error) {
                self.postMessage({ type: 'init', success: false, error: error.message });
            }
            break;
            
        case 'process':
            try {
                // processImage is a synchronous method
                const result = engine.processImage(
                    data.imageData, 
                    data.options.width, 
                    data.options.height, 
                    data.options.processMode || ProcessMode.Video
                );
                self.postMessage({ 
                    type: 'process', 
                    success: true, 
                    result: result 
                });
            } catch (error) {
                self.postMessage({ 
                    type: 'process', 
                    success: false, 
                    error: error.message 
                });
            }
            break;
    }
};

Architecture Design

1. Singleton Pattern for Engine Management

javascript
class BeautyEngineManager {
    static instance = null;
    static engine = null;
    
    static async getInstance(config) {
        if (!this.instance) {
            this.instance = new BeautyEngineManager();
            await this.instance.init(config);
        }
        return this.instance;
    }
    
    async init(config) {
        if (!this.engine) {
            const engineConfig = new EngineConfig(config);
            this.engine = new BeautyEffectEngine(engineConfig);
            await this.engine.init();
        }
    }
    
    getEngine() {
        return this.engine;
    }
    
    destroy() {
        if (this.engine) {
            this.engine.destroy();
            this.engine = null;
        }
        this.instance = null;
    }
}

// Usage
const manager = await BeautyEngineManager.getInstance({
    appId: 'your-app-id',
    appKey: 'your-app-key'
});
const engine = manager.getEngine();

2. Parameter Management Class

javascript
class BeautyParamManager {
    constructor(engine) {
        this.engine = engine;
        this.params = {
            basic: {
                smoothing: 0,
                whitening: 0,
                rosiness: 0
            },
            reshape: {
                faceThin: 0,
                eyeSize: 0
            },
            makeup: {
                lipstick: 0,
                blush: 0
            }
        };
    }
    
    setBasicParam(type, value) {
        this.params.basic[type] = value;
        this.engine.setBasicParam(BasicParam[type], value);
    }
    
    setReshapeParam(type, value) {
        this.params.reshape[type] = value;
        this.engine.setReshapeParam(ReshapeParam[type], value);
    }
    
    setMakeupParam(type, value) {
        this.params.makeup[type] = value;
        this.engine.setMakeupParam(MakeupParam[type], value);
    }
    
    reset() {
        Object.keys(this.params.basic).forEach(key => {
            this.setBasicParam(key, 0);
        });
        Object.keys(this.params.reshape).forEach(key => {
            this.setReshapeParam(key, 0);
        });
        Object.keys(this.params.makeup).forEach(key => {
            this.setMakeupParam(key, 0);
        });
    }
    
    getParams() {
        return JSON.parse(JSON.stringify(this.params));
    }
}

3. Image Processing Pipeline

javascript
class ImageProcessingPipeline {
    constructor(engine) {
        this.engine = engine;
        this.canvas = document.createElement('canvas');
        this.ctx = this.canvas.getContext('2d');
    }
    
    async processImage(source, options = {}) {
        try {
            // 1. Prepare Canvas
            this.canvas.width = source.width || source.videoWidth;
            this.canvas.height = source.height || source.videoHeight;
            
            // 2. Draw source image
            this.ctx.drawImage(source, 0, 0);
            
            // 3. Get image data
            const imageData = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);
            
            // 4. Process image (synchronous method, no await needed)
            const result = this.engine.processImage(
                imageData, 
                this.canvas.width, 
                this.canvas.height, 
                options.processMode || ProcessMode.Video
            );
            
            // 5. Draw result
            this.ctx.putImageData(result, 0, 0);
            
            return this.canvas;
        } catch (error) {
            console.error('Image processing failed:', error);
            throw error;
        }
    }
}

User Experience Optimization

1. Loading Status Indication

javascript
async function initEngineWithLoading(config) {
    showLoading('Initializing beauty engine...');
    
    try {
        const engine = new BeautyEffectEngine(config);
        await engine.init();
        hideLoading();
        showSuccess('Beauty engine initialized successfully');
        return engine;
    } catch (error) {
        hideLoading();
        showError('Beauty engine initialization failed: ' + error.message);
        throw error;
    }
}

2. Progressive Loading

javascript
async function initEngineProgressive(config) {
    // 1. Show loading prompt
    updateLoadingStatus('Loading WASM module...');
    
    const engine = new BeautyEffectEngine(config);
    
    // 2. Initialize (will load WASM internally)
    updateLoadingStatus('Initializing engine...');
    await engine.init();
    
    // 3. Enable beauty types
    updateLoadingStatus('Configuring beauty parameters...');
    engine.setBeautyTypeEnabled(BeautyType.Basic, true);
    
    updateLoadingStatus('Complete');
    return engine;
}

3. Error Retry Mechanism

javascript
async function initEngineWithRetry(config, maxRetries = 3) {
    for (let i = 0; i < maxRetries; i++) {
        try {
            const engine = new BeautyEffectEngine(config);
            await engine.init();
            return engine;
        } catch (error) {
            if (i < maxRetries - 1) {
                console.warn(`Initialization failed, retrying in ${1000 * (i + 1)}ms...`);
                await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
            } else {
                throw error;
            }
        }
    }
}

Security Recommendations

1. Protect AppID and AppKey

Don't hardcode keys in frontend code:

javascript
// ❌ Not recommended: hardcoded in code
const config = new EngineConfig({
    appId: 'your-app-id',
    appKey: 'your-app-key'
});

// ✅ Recommended: fetch from server or use environment variables
async function getConfig() {
    const response = await fetch('/api/config');
    const { appId, appKey } = await response.json();
    return new EngineConfig({ appId, appKey });
}

2. Use HTTPS

Ensure using HTTPS in production environment to protect data transmission security.

3. Validate User Input

javascript
function validateConfig(config) {
    if (!config.appId || typeof config.appId !== 'string') {
        throw new Error('appId must be a string');
    }
    
    if (!config.appKey || typeof config.appKey !== 'string') {
        throw new Error('appKey must be a string');
    }
    
    return true;
}