How to Choose a Document Capture Solution
for Digitizing Paper Records

An interactive comparison guide — try each approach live

Decision Framework

Answer these questions to narrow down which approach fits your project. Your recommendation updates as you answer.

Q1: What's your daily scanning volume?

Q2: What kinds of documents?

Q3: Where are your users?

Q4: What's your integration architecture?

Approach Comparison

Criterion Physical Scanner
TWAIN / WIA / SANE
Camera-Based
Mobile / Overhead
Network / Remote
REST API
Best for High-volume, ADF-fed stacks Bound books, fragile docs, mobile Shared-office, multi-user access
Throughput 20–200 ppm with duplex ~5–15 pages/min (manual) Depends on scanner
Image quality Consistent 200–600 DPI Variable (lighting/angle) Same as physical scanner
Document types Loose sheets only (ADF) Any — bound, fragile, odd sizes Loose sheets (scanner-dependent)
Client setup TWAIN driver on each PC Browser only Browser only (REST endpoint)
Offline support Yes (local driver) Yes (browser-side) No (needs network)
Multi-user One user per scanner One device, one user Many users share one scanner
SDK Dynamic Web TWAIN Mobile Web Capture DWT Service REST API

Try Each Approach

All three demos share the same Dynamsoft license. A 30-day public trial key is pre-filled; replace it with your own if you have one.

✓ Public trial key active

Scanned pages will appear here

Make sure the Dynamsoft Service is installed and running.

Camera preview & captures will appear here

Click "Start Camera" to begin, then "Capture Document" to grab the current frame.
Base URL without /api — it's appended automatically
API Endpoints Being Called (v19+)
GET    {host}/api/device/scanners                       — List available scanners
POST   {host}/api/device/scanners/jobs                   — Create a scan job (auto)
       Header: DWT-PRODUCT-KEY: {your-license}
GET    {host}/api/device/scanners/jobs/{id}/next-page?type=image/png — Get page image
DELETE {host}/api/device/scanners/jobs/{id}               — Delete job
                            

Fetched Pages

Scanned page images will appear here

Click "Discover Scanners" → select a scanner → "Scan & Fetch Images".
Raw API Log
{
  "message": "Click 'Discover Scanners' to begin.
               The DWT Service must be running on the host machine."
}

Code Examples

1. TWAIN Scanner via Dynamic Web TWAIN JavaScript

// Initialize and scan with ADF + duplex (DWT v19+)
Dynamsoft.DWT.ProductKey = "YOUR-LICENSE-KEY";
Dynamsoft.DWT.ResourcesPath = "https://cdn.jsdelivr.net/npm/dwt@19.4.1/dist";
Dynamsoft.DWT.AutoLoad = false;

Dynamsoft.DWT.CreateDWTObjectEx(
    { WebTwainId: "dwtScanner" },
    function (obj) {
        // Headless mode — bind viewer to your own container
        obj.Viewer.bind("dwt-container");
        obj.Viewer.show();

        obj.GetDevicesAsync().then(function (devices) {
            return obj.SelectDeviceAsync(devices[0]);
        }).then(function () {
            return obj.AcquireImageAsync({
                IfShowUI: false,
                PixelType: 2, Resolution: 300,
                IfFeederEnabled: true,
                IfDuplexEnabled: true,
                IfCloseSourceAfterAcquire: true,
            });
        }).then(function () {
            // Get a URL for the latest scanned image
            var lastIndex = obj.HowManyImagesInBuffer - 1;
            var url = obj.GetImageURL(lastIndex);
            document.getElementById("preview").src = url;
        });
    },
    function (error) { console.error(error.code, error.message); }
);

2. Camera Capture with DCV Bundle JavaScript

// Initialize license + camera (getUserMedia) + document detection
await Dynamsoft.License.LicenseManager.initLicense("YOUR-KEY", true);
await Dynamsoft.Core.CoreModule.loadWasm(["DDN"]);

const cvr = await Dynamsoft.CVR.CaptureVisionRouter.createInstance();

// Open camera via native getUserMedia
const stream = await navigator.mediaDevices.getUserMedia({
    video: { width: { ideal: 1920 }, height: { ideal: 1080 } }
});
videoElement.srcObject = stream;

// Detection loop using requestAnimationFrame
async function scanLoop() {
    const result = await cvr.capture(videoElement, "DetectDocumentBoundaries_Default");
    const quad = result.items?.[0]?.location?.points; // detected boundary
    if (quad && isStable(quad, previousQuad)) {
        // Normalize: correct perspective and crop
        const settings = await cvr.getSimplifiedSettings("NormalizeDocument_Default");
        settings.roi.points = quad;
        settings.roiMeasuredInPercentage = 0;
        await cvr.updateSettings("NormalizeDocument_Default", settings);

        // Capture current frame and normalize
        const frameCanvas = captureFrame(videoElement);
        const normResult = await cvr.capture(frameCanvas, "NormalizeDocument_Default");
        const canvas = normResult.items[0].toCanvas(); // final document
    }
    requestAnimationFrame(scanLoop);
}

3. Remote Scanner via REST API JavaScript

// List scanners on a remote machine running DWT Service (v19+)
const host = "http://scanner-host:18622/api";
const scanners = await (await fetch(host + "/device/scanners")).json();

// Populate scanner dropdown for user selection
scanners.forEach(s => addOption(s.name));

// When user clicks "Scan": create job + fetch images in one flow
const scanner = scanners[selectedIdx];
const job = await (await fetch(host + "/device/scanners/jobs", {
    method: "POST",
    headers: {
        "Content-Type": "application/json",
        "DWT-PRODUCT-KEY": "YOUR-LICENSE-KEY"
    },
    body: JSON.stringify({
        autoRun: true,
        device: scanner.device,
        name: scanner.name,
        config: { Resolution: 300, PixelType: 2, IfFeederEnabled: true }
    })
})).json();

// Fetch each scanned page image as PNG bytes
while (true) {
    const imgRes = await fetch(
        host + "/device/scanners/jobs/" + job.jobuid + "/next-page?type=image/png",
        { headers: { "DWT-PRODUCT-KEY": "YOUR-LICENSE-KEY" } }
    );
    if (imgRes.status === 204) break; // no more pages
    const blob = await imgRes.blob();
    // display blob via URL.createObjectURL(blob)
}

// Clean up
await fetch(host + "/device/scanners/jobs/" + job.jobuid, { method: "DELETE" });

Real-World Combinations

🏥

Hospital Records

Physical scanner (ADF) for loose patient records + Camera capture for bound logbooks and oversized charts.

Physical + Camera
⚖️

Law Firm e-Discovery

Remote scanning via REST API — all lawyers scan to a central repository from their browsers, with scanner locking.

REST API
🏫

School Exam Archive

Physical scanner for answer sheets → OCR → barcode indexing → searchable PDF archive.

Physical + OCR
📱

Remote Notary

Camera-based capture on mobile for on-the-go document digitization, instant upload to DMS.

Camera