# Stop Leaking PII! Local Data Masking with Transformers.js and WASM

> Source: <https://dev.to/beck_moulton/stop-leaking-pii-local-data-masking-with-transformersjs-and-wasm-344l>
> Published: 2026-08-02 00:20:00+00:00

In an era where data privacy is no longer a "nice-to-have" but a legal mandate (looking at you, GDPR and HIPAA), sending raw user data to the cloud is like playing with fire. If you are building health-tech or fintech apps, the risk of exposing **Personally Identifiable Information (PII)** is a constant headache.

But what if the data never leaves the user's browser in its raw form? Enter **Edge AI** and **Privacy-preserving AI**. By leveraging **Transformers.js** and **WebAssembly (WASM)**, we can perform complex **Named Entity Recognition (NER)** to de-identify sensitive information directly on the client side.

In this tutorial, we’ll build a "Privacy Shield" that detects and masks names, locations, and health identifiers before they ever hit your API.

The traditional approach involves sending raw text to a server-side LLM or NLP service. Our approach intercepts the data at the "Edge" (the browser).

``` php
graph TD
    A[User Inputs Sensitive Health Data] --> B{Browser-side Privacy Shield}
    B --> C[Transformers.js / WASM]
    C --> D[NER Model Analysis]
    D --> E[Data Masking / Redaction]
    E --> F[Clean Data]
    F --> G[Cloud Storage / Analytics]
    G -.-> H[Compliance & Security ✅]

    style B fill:#f9f,stroke:#333,stroke-width:2px
    style C fill:#bbf,stroke:#333,stroke-width:2px
```

By using **WebAssembly**, we get near-native performance for running BERT-based models in the browser, ensuring the UI remains snappy while keeping the data 100% local.

To follow along, you'll need:

First, let's install the library:

```
npm install @xenova/transformers
```

Now, let's create our `PrivacyShield`

service. We will use a lightweight NER model (like `Xenova/bert-base-NER`

) that has been optimized for the web.

``` js
// src/services/privacyShield.ts
import { pipeline, env } from '@xenova/transformers';

// Optimization for browser environment
env.allowLocalModels = false;
env.useBrowserCache = true;

export class PrivacyShield {
    private classifier: any = null;

    async init() {
        // Initialize the Token Classification pipeline (NER)
        this.classifier = await pipeline('token-classification', 'Xenova/bert-base-NER');
        console.log("🚀 Privacy Engine Ready!");
    }

    async maskPII(text: string): Promise<string> {
        if (!this.classifier) await this.init();

        // Perform Named Entity Recognition
        const output = await this.classifier(text);

        // Sort entities by index in reverse to prevent string offset issues
        const entities = output.sort((a: any, b: any) => b.start - a.start);

        let maskedText = text;

        for (const entity of entities) {
            // Only mask sensitive labels: PER (Person), LOC (Location), ORG (Organization)
            if (['PER', 'LOC', 'ORG'].includes(entity.entity)) {
                const mask = `[${entity.entity}]`;
                maskedText = maskedText.slice(0, entity.start) + mask + maskedText.slice(entity.end);
            }
        }

        return maskedText;
    }
}
```

Imagine a user typing their symptoms: *"My name is Alice Smith, and I've been feeling dizzy since I visited the clinic in New York."* We need to mask "Alice Smith" and "New York" before syncing.

``` js
// src/main.ts
import { PrivacyShield } from './services/privacyShield';

const shield = new PrivacyShield();
const rawInput = "Patient: John Doe. Location: London. Symptoms: Severe headache.";

async function handleDataUpload(data: string) {
    console.log("🔒 Original:", data);

    // De-identify on the client side!
    const cleanData = await shield.maskPII(data);

    console.log("🛡️ Masked:", cleanData);
    // Output: Patient: [PER]. Location: [LOC]. Symptoms: Severe headache.

    // Now it's safe to upload to your cloud!
    await uploadToCloud(cleanData);
}

handleDataUpload(rawInput);
```

You might wonder, "Why not just use Regex?" Regex is great for patterns (like emails or SSNs), but it fails miserably at detecting names or locations in natural language.

By using **Transformers.js with WebAssembly**, we run a real deep-learning model. WASM allows the browser to execute binary code at high speeds, making it possible to run a 100MB+ BERT model in milliseconds after the initial load.

For more advanced patterns on handling large-scale Edge AI deployments or building production-ready de-identification pipelines, I highly recommend checking out the deep dives over at ** wellally.tech/blog**. They cover great architectural blueprints for "Privacy by Design" that go beyond just the browser.

`distilbert`

or `tiny-bert`

versions to keep the initial download under 20MB.`PrivacyShield`

in a Building "Privacy First" isn't just about compliance; it's about building trust with your users. By implementing **Edge AI de-identification**, you eliminate the risk of PII leakage before the data even touches your infrastructure.

**Summary of benefits:**

Are you using AI at the edge yet? If you found this helpful, drop a comment below or share your experience with Transformers.js! Happy coding! 💻🔥

*For more production-ready examples of Edge AI and data security, visit the official blog at wellally.tech/blog.*
