WebAssembly Examples¶
Complete WebAssembly examples demonstrating fastlowess-wasm for browser-based smoothing.
Batch Smoothing¶
Process complete datasets in the browser.
<!--
fastlowess Batch Smoothing Example
This example demonstrates batch LOWESS smoothing features:
- Basic smoothing with different parameters
- Robustness iterations for outlier handling
- Confidence and prediction intervals
- Diagnostics and cross-validation
The batch adapter (smooth function) is the primary interface for
processing complete datasets that fit in memory.
-->
<!DOCTYPE html>
<html>
<head>
<title>fastlowess WASM - Batch Smoothing Example</title>
<style>
body {
font-family: monospace;
max-width: 900px;
margin: 0 auto;
padding: 20px;
background: #1e1e1e;
color: #d4d4d4;
}
h1 {
color: #569cd6;
}
#output {
white-space: pre-wrap;
font-family: 'Consolas', 'Monaco', monospace;
}
.log-section {
margin-bottom: 20px;
border-left: 2px solid #569cd6;
padding-left: 10px;
}
button {
cursor: pointer;
padding: 8px 16px;
font-size: 14px;
background: #0e639c;
color: white;
border: none;
border-radius: 4px;
}
button:hover {
background: #1177bb;
}
</style>
</head>
<body>
<h1>fastlowess Batch Smoothing Example</h1>
<p>Running WASM implementation...</p>
<div id="output">Initializing...</div>
<script type="module">
import init, { smooth } from "../../bindings/wasm/pkg-web/fastlowess_wasm.js";
// Helper to log to screen
function log(msg) {
const out = document.getElementById('output');
out.innerText += msg + "\n";
console.log(msg);
}
function clearLog() {
document.getElementById('output').innerText = "";
}
// 1. Generate Data
function generateSampleData(nPoints = 1000) {
const x = new Float64Array(nPoints);
const y = new Float64Array(nPoints);
for (let i = 0; i < nPoints; i++) {
x[i] = (i / (nPoints - 1)) * 50; // range 0 to 50
// Trend + Seasonality
const yTrue = 0.5 * x[i] + 5 * Math.sin(x[i] * 0.5);
// Gaussian noise (approx)
let u1 = Math.random();
let u2 = Math.random();
let z = Math.sqrt(-2.0 * Math.log(u1)) * Math.cos(2.0 * Math.PI * u2);
y[i] = yTrue + z * 1.5;
}
// Add significant outliers (10% of data)
const nOutliers = Math.round(nPoints * 0.1);
for (let k = 0; k < nOutliers; k++) {
const idx = Math.floor(Math.random() * nPoints);
const sign = Math.random() < 0.5 ? -1 : 1;
y[idx] += (10 + Math.random() * 10) * sign;
}
return { x, y };
}
async function runDemo() {
try {
clearLog();
log("=== fastlowess Batch Smoothing Example ===\n");
await init();
// 1. Generate Data
const { x, y } = generateSampleData(1000);
log(`1. Generated ${x.length} data points with outliers.`);
// 2. Basic Smoothing (Default parameters)
log("\n2. Running basic smoothing...");
const startBasic = performance.now();
const resBasic = smooth(x, y, { iterations: 0, fraction: 0.05 });
log(` - Completed in ${(performance.now() - startBasic).toFixed(2)}ms`);
// 3. Robust Smoothing (IRLS)
log("\n3. Running robust smoothing (3 iterations)...");
const resRobust = smooth(x, y, {
fraction: 0.05,
iterations: 3,
robustnessMethod: "bisquare",
returnRobustnessWeights: true
});
const weights = resRobust.robustnessWeights;
let outlierCount = 0;
if (weights) {
for (let i = 0; i < weights.length; i++) {
if (weights[i] < 0.1) outlierCount++;
}
}
log(` - Identified ~${outlierCount} outliers (weight < 0.1)`);
// 4. Uncertainty Quantification
log("\n4. Computing confidence and prediction intervals...");
const resIntervals = smooth(x, y, {
fraction: 0.05,
confidenceIntervals: 0.95,
predictionIntervals: 0.95,
returnDiagnostics: true
});
if (resIntervals.diagnostics) {
const d = resIntervals.diagnostics;
log(` - Fit Statistics:`);
log(` R²: ${d.rSquared.toFixed(4)}`);
log(` RMSE: ${d.rmse.toFixed(4)}`);
log(` MAE: ${d.mae.toFixed(4)}`);
}
// 5. Cross-Validation for optimal fraction
log("\n5. Running cross-validation to find optimal fraction...");
const cvFractions = [0.05, 0.1, 0.2, 0.4];
const resCV = smooth(x, y, {
cvFractions,
cvMethod: "kfold",
cvK: 5
});
log(` - Optimal fraction selected: ${resCV.fractionUsed}`);
if (resCV.cvScores) {
log(` - CV Scores: [${Array.from(resCV.cvScores).map(n => n.toFixed(4)).join(', ')}]`);
}
// 6. Boundary Policy Comparison
log("\n6. Demonstrating boundary policy effects on linear data...");
const xl = new Float64Array(50);
const yl = new Float64Array(50);
for (let i = 0; i < 50; i++) {
xl[i] = (i / 49) * 10;
yl[i] = 2 * xl[i] + 1;
}
const rExt = smooth(xl, yl, { fraction: 0.6, boundaryPolicy: "extend" });
const rRef = smooth(xl, yl, { fraction: 0.6, boundaryPolicy: "reflect" });
const rZr = smooth(xl, yl, { fraction: 0.6, boundaryPolicy: "zero" });
log(` - Extend (Default): first=${rExt.y[0].toFixed(2)}, last=${rExt.y[49].toFixed(2)}`);
log(` - Reflect: first=${rRef.y[0].toFixed(2)}, last=${rRef.y[49].toFixed(2)}`);
log(` - Zero: first=${rZr.y[0].toFixed(2)}, last=${rZr.y[49].toFixed(2)}`);
log("\n=== Batch Smoothing Example Complete ===");
} catch (e) {
log(`\nError: ${e}`);
console.error(e);
}
}
// Start execution
runDemo();
</script>
</body>
</html>
Streaming Smoothing¶
Process large datasets in memory-efficient chunks in the browser.
<!DOCTYPE html>
<html>
<head>
<title>fastlowess WASM - Streaming Smoothing</title>
<style>
body {
font-family: sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.controls {
margin: 20px 0;
padding: 10px;
background: #f0f0f0;
border-radius: 4px;
}
#output {
white-space: pre-wrap;
background: #fafafa;
border: 1px solid #ddd;
padding: 10px;
}
button {
cursor: pointer;
padding: 5px 10px;
}
</style>
</head>
<body>
<h1>Streaming Smoothing Example</h1>
<p>Using <code>StreamingLowessWasm</code> to process large data in chunks in the browser.</p>
<div class="controls">
<button id="runBtn">Start Streaming Process</button>
</div>
<h3>Output Logs:</h3>
<div id="output">Ready.</div>
<script type="module">
import init, { StreamingLowessWasm } from "../../bindings/wasm/pkg-web/fastlowess_wasm.js";
async function run() {
try {
await init();
log("WASM Initialized.");
document.getElementById('runBtn').addEventListener('click', startStreaming);
} catch (e) {
log("Error: " + e);
}
}
async function startStreaming() {
try {
log("\nStarting streaming process...");
// Initialize Streamer
const streamer = new StreamingLowessWasm(
{ fraction: 0.1 },
{ chunkSize: 1000, overlap: 100 }
);
// Simulate processing 10 chunks
const chunkSize = 1000;
let totalPoints = 0;
log("Processing chunks...");
const start = performance.now();
for (let i = 0; i < 10; i++) {
// Generate random chunk
const x = new Float64Array(chunkSize);
const y = new Float64Array(chunkSize);
for (let j = 0; j < chunkSize; j++) {
const globalIdx = i * chunkSize + j;
x[j] = globalIdx;
y[j] = Math.log(globalIdx + 1) + Math.random();
}
// Process
const res = streamer.processChunk(x, y);
if (res && res.y) {
totalPoints += res.y.length;
}
// Small delay to let UI update (simulate async stream)
if (i % 2 === 0) await new Promise(r => setTimeout(r, 10));
}
const finalRes = streamer.finalize();
if (finalRes && finalRes.y) totalPoints += finalRes.y.length;
const end = performance.now();
log(`Streaming complete.`);
log(`Total output points: ${totalPoints}`);
log(`Time taken: ${(end - start).toFixed(2)}ms`);
} catch (e) {
log("Error: " + e);
console.error(e);
}
}
function log(msg) {
const out = document.getElementById('output');
out.innerText += msg + "\n";
console.log(msg);
}
run();
</script>
</body>
</html>
Download streaming_smoothing.html
Online Smoothing¶
Real-time smoothing with sliding window for browser applications.
<!DOCTYPE html>
<html>
<head>
<title>fastlowess WASM - Online Smoothing</title>
<style>
body {
font-family: sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.controls {
margin: 20px 0;
padding: 10px;
background: #f0f0f0;
border-radius: 4px;
}
#output {
white-space: pre-wrap;
background: #fafafa;
border: 1px solid #ddd;
padding: 10px;
height: 300px;
overflow-y: scroll;
}
button {
cursor: pointer;
padding: 5px 10px;
}
.metric {
font-weight: bold;
color: #0066cc;
}
</style>
</head>
<body>
<h1>Online Smoothing Example</h1>
<p>Using <code>OnlineLowessWasm</code> for real-time data smoothing.</p>
<div class="controls">
<button id="startBtn">Start Simulation</button>
<button id="stopBtn" disabled>Stop</button>
<span id="status">Idle</span>
</div>
<h3>Live Logs:</h3>
<div id="output">Waiting to start...</div>
<script type="module">
import init, { OnlineLowessWasm } from "../../bindings/wasm/pkg-web/fastlowess_wasm.js";
let running = false;
let onlineModel = null;
let t = 0;
async function run() {
try {
await init();
log("WASM Initialized.");
document.getElementById('startBtn').addEventListener('click', startSim);
document.getElementById('stopBtn').addEventListener('click', stopSim);
} catch (e) {
log("Error: " + e);
}
}
function startSim() {
running = true;
document.getElementById('startBtn').disabled = true;
document.getElementById('stopBtn').disabled = false;
document.getElementById('status').innerText = "Running...";
// Initialize new model
onlineModel = new OnlineLowessWasm(
{ fraction: 0.2 },
{ windowCapacity: 50, updateMode: "incremental" }
);
t = 0;
log("\n=== Starting Real-time Simulation ===");
requestAnimationFrame(step);
}
function stopSim() {
running = false;
document.getElementById('startBtn').disabled = false;
document.getElementById('stopBtn').disabled = true;
document.getElementById('status').innerText = "Stopped";
log("=== Simulation Stopped ===");
}
function step() {
if (!running) return;
// Generate one point
const noise = (Math.random() - 0.5) * 2.0;
const signal = 10 * Math.sin(t * 0.1);
const y = signal + noise;
// Update model
const smoothed = onlineModel.update(t, y);
// Log output
if (smoothed !== undefined && smoothed !== null) {
log(`t=${t}, raw=${y.toFixed(2)}, smoothed=${smoothed.toFixed(2)}`);
} else {
log(`t=${t}, raw=${y.toFixed(2)}, smoothed=(insufficient data)`);
}
t++;
// Slow down loop for visibility
setTimeout(() => {
// Auto-stop after 200 points
if (t >= 200) {
stopSim();
log("=== Auto-stop limit reached (200 points) ===");
} else if (running) {
requestAnimationFrame(step);
}
}, 100);
}
function log(msg) {
const out = document.getElementById('output');
out.innerText = msg + "\n" + out.innerText;
if (out.innerText.length > 5000) out.innerText = out.innerText.substring(0, 5000); // Limit buffer
}
run();
</script>
</body>
</html>
Download online_smoothing.html
Installation¶
NPM¶
CDN¶
<script type="module">
import init, { smooth } from 'https://unpkg.com/fastlowess-wasm@latest';
await init();
// Ready to use
</script>
Quick Start¶
Browser (ES Modules)¶
import init, { smooth, smoothStreaming, smoothOnline } from 'fastlowess-wasm';
async function main() {
// Initialize WASM module
await init();
// Generate sample data
const x = Float64Array.from({ length: 100 }, (_, i) => i * 0.1);
const y = Float64Array.from(x, xi => Math.sin(xi) + Math.random() * 0.2);
// Basic smoothing
const result = smooth(x, y, { fraction: 0.3 });
console.log('Smoothed values:', result.y);
// With options
const resultWithOptions = smooth(x, y, {
fraction: 0.3,
iterations: 3,
confidenceIntervals: 0.95,
returnDiagnostics: true
});
console.log('R²:', resultWithOptions.diagnostics.rSquared);
}
main();
Node.js¶
const { smooth } = require('fastlowess-wasm');
// Same API as browser
const result = smooth(x, y, { fraction: 0.3 });
Features¶
The WebAssembly bindings provide:
- Zero dependencies - Pure WASM, no runtime requirements
- TypedArray support - Works with
Float64Arrayfor efficiency - Same API as Node.js - Consistent interface across platforms
- Small bundle size - Optimized with
wasm-opt