<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rahul Sangamker</title>
    <description>The latest articles on DEV Community by Rahul Sangamker (@rahul_sangamker_653e0c1ba).</description>
    <link>https://dev.to/rahul_sangamker_653e0c1ba</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3978385%2F799e0eb2-5f35-458a-bae0-0fc4d65d9490.png</url>
      <title>DEV Community: Rahul Sangamker</title>
      <link>https://dev.to/rahul_sangamker_653e0c1ba</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rahul_sangamker_653e0c1ba"/>
    <language>en</language>
    <item>
      <title>From Keypoints to Measurements: Why Landmarks Alone Are Useless</title>
      <dc:creator>Rahul Sangamker</dc:creator>
      <pubDate>Wed, 10 Jun 2026 21:49:57 +0000</pubDate>
      <link>https://dev.to/rahul_sangamker_653e0c1ba/from-keypoints-to-measurements-why-landmarks-alone-are-useless-4oec</link>
      <guid>https://dev.to/rahul_sangamker_653e0c1ba/from-keypoints-to-measurements-why-landmarks-alone-are-useless-4oec</guid>
      <description>&lt;p&gt;&lt;em&gt;Every hand-tracking demo shows you 21 dots. The interesting part is what nobody shows: turning dots into numbers someone can act on.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dots are a capability, not a product
&lt;/h2&gt;

&lt;p&gt;Run any modern hand-tracking model and you get 21 beautifully stable landmarks per hand at 30 FPS. Impressive, and by itself worthless. No client has ever paid for dots. They pay for &lt;em&gt;measurements&lt;/em&gt;: is this clearance compliant, is this part aligned, did this patient's range of motion improve.&lt;/p&gt;

&lt;p&gt;I learned this on utility infrastructure work, where the deliverable was never "we detected the wire". It was &lt;em&gt;the attachment height of that wire, and whether it violates clearance rules&lt;/em&gt;. Keypoints were step one of three.&lt;/p&gt;

&lt;h2&gt;
  
  
  The demo: live metrics, not just a skeleton
&lt;/h2&gt;

&lt;p&gt;My portfolio's keypoint demo derives three measurements per hand, every frame:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wrist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lm&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;palm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lm&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;          &lt;span class="c1"&gt;// scale reference&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pinch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lm&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;lm&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;palm&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// thumb tip ↔ index tip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The crucial line is the &lt;strong&gt;scale reference&lt;/strong&gt;. Pixel distances are meaningless; they change as you move toward the camera. Dividing by palm length (wrist to middle knuckle) gives a &lt;em&gt;relative&lt;/em&gt; measurement that's stable under distance, and multiplying by the average adult palm length (~8.5 cm) converts it into an approximate real-world gap. The demo shows "≈ 3.2 cm" floating on the pinch line. In infrastructure work the same role is played by a known object dimension: a standard crossarm, a pole class height. Every measurement-from-pixels system needs its ruler.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvsb44xj5c6r3nvo8yhqx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvsb44xj5c6r3nvo8yhqx.png" alt="Hand skeleton with pinch ruler and the landmark-to-decision measurement ladder" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finger counting is a geometric test (is each fingertip farther from the wrist than its middle joint?), and "hand openness" averages fingertip extension. Three lines of geometry each, but they convert a model output into a readout a human understands instantly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest layering
&lt;/h2&gt;

&lt;p&gt;The landmarks come from MediaPipe's pretrained pipeline (palm detector → landmark regressor → gesture classifier, float16, WASM + GPU delegate). They're Google's models, credited on the page. The engineering I own is the integration (lazy loading, render loop, throttled UI) and the measurement layer on top. Knowing &lt;em&gt;when&lt;/em&gt; a pretrained model suffices and when you need to fine-tune your own (as I did for utility keypoints, where off-the-shelf models had never seen a crossarm) is most of the senior judgment in applied CV.&lt;/p&gt;

&lt;h2&gt;
  
  
  The hard problems hiding behind the dots
&lt;/h2&gt;

&lt;p&gt;The demo derives three measurements with three lines of geometry each. Production measurement systems earn their keep on the problems the demo deliberately sidesteps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Metric scale from a single camera.&lt;/strong&gt; Palm length is a convenient ruler, but it assumes an average hand. Real systems calibrate against a known object, use stereo or depth sensors, or exploit multi-frame geometry. Choosing the ruler is the core design decision in every measurement-from-pixels system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jitter becomes error bars.&lt;/strong&gt; Landmarks vibrate frame to frame. Filtering trades latency for stability (the one-euro filter is the workhorse), and the residual jitter should propagate into the output as explicit uncertainty: not "3.2 cm" but "3.2 plus or minus 0.3 cm". People make different decisions when they can see the error bars.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When the pretrained model stops being enough.&lt;/strong&gt; Hands are the best-served keypoint domain in the world. Utility crossarms are not. The judgment call between adapting a pretrained model, fine-tuning on custom annotations, and training from scratch is driven by how far your structures sit from the model's training distribution, and it is most of the senior work in applied CV.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Temporal measurements.&lt;/strong&gt; Static distances are the beginning. Velocities, ranges of motion, and repetition quality come from trajectories over time, which is where measurement systems start replacing human assessment instead of assisting it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The pattern to steal
&lt;/h2&gt;

&lt;p&gt;Whatever your domain: &lt;strong&gt;landmark → scale reference → relative measurement → threshold → decision.&lt;/strong&gt; That last hop, from number to decision, is where the business value lives. Models are increasingly commodities; measurement systems are not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it&lt;/strong&gt; (pinch slowly and watch the number): &lt;a href="https://rs-03.github.io/demos/#keypoints" rel="noopener noreferrer"&gt;rs-03.github.io/demos&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Source&lt;/strong&gt;: &lt;a href="https://github.com/rs-03/rs-03.github.io" rel="noopener noreferrer"&gt;github.com/rs-03/rs-03.github.io&lt;/a&gt;&lt;/p&gt;

</description>
      <category>computervision</category>
      <category>machinelearning</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>I Put a Neural Network Inside My Portfolio: No TensorFlow, No Server, 145 KB</title>
      <dc:creator>Rahul Sangamker</dc:creator>
      <pubDate>Wed, 10 Jun 2026 21:49:54 +0000</pubDate>
      <link>https://dev.to/rahul_sangamker_653e0c1ba/i-put-a-neural-network-inside-my-portfolio-no-tensorflow-no-server-145-kb-32k7</link>
      <guid>https://dev.to/rahul_sangamker_653e0c1ba/i-put-a-neural-network-inside-my-portfolio-no-tensorflow-no-server-145-kb-32k7</guid>
      <description>&lt;p&gt;&lt;em&gt;Training a network from scratch in raw NumPy, quantizing it to int8, and running it as ~80 lines of dependency-free JavaScript, with a parity test proving the browser matches Python to 1e-6.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why bother? MNIST is a solved problem
&lt;/h2&gt;

&lt;p&gt;Digit recognition is the "hello world" of ML, and that's exactly why I used it. The model isn't the point. The point is everything around the model, which happens to be the part that matters in production work too: training without a framework, compressing for deployment, running inference in a constrained environment, and &lt;em&gt;proving&lt;/em&gt; the deployed system matches the trained one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Training: just NumPy and math
&lt;/h2&gt;

&lt;p&gt;The network is a 784→128→64→10 MLP: hand-written forward pass, backpropagation, and Adam optimizer. No autograd, no framework:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# backward pass, by hand
&lt;/span&gt;&lt;span class="n"&gt;dz3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;probs&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;y_batch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;batch_size&lt;/span&gt;
&lt;span class="n"&gt;grads_w&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;dz3&lt;/span&gt;
&lt;span class="n"&gt;da2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dz3&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;
&lt;span class="n"&gt;dz2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;da2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z2&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="c1"&gt;# ReLU mask
&lt;/span&gt;&lt;span class="n"&gt;grads_w&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;dz2&lt;/span&gt;
&lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs33da7l1kkxp5j97y4jo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs33da7l1kkxp5j97y4jo.png" alt="784-128-64-10 network trained in raw NumPy, int8-quantized, run in the browser"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One trick that matters for a &lt;em&gt;drawing&lt;/em&gt; demo specifically: &lt;strong&gt;shift augmentation&lt;/strong&gt;. MNIST digits are centered; humans draw wherever they like. Training on randomly translated copies makes the model tolerant of sloppy placement. Combined with MNIST-style preprocessing at inference (crop to bounding box, scale into a 20×20 box, center by center-of-mass), real-world doodles classify reliably. Final test accuracy: &lt;strong&gt;98.2%&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compression: int8 in 15 lines
&lt;/h2&gt;

&lt;p&gt;A float32 weight file would be ~430 KB. Symmetric int8 quantization cuts it ~4×:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;scale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;127.0&lt;/span&gt;
&lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;127&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;127&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One scale factor per layer, weights stored as base64 in JSON: &lt;strong&gt;145 KB total&lt;/strong&gt;, and quantized test accuracy is &lt;em&gt;identical&lt;/em&gt; to float: 98.2%.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inference: ~80 lines of plain JavaScript
&lt;/h2&gt;

&lt;p&gt;In the browser, the weights are dequantized once on load, and inference is three matrix-vector products with ReLU and a softmax. ~109K multiply-adds, about a microsecond-scale problem for any modern device. No TensorFlow.js (that runtime is megabytes; the entire model is 145 KB).&lt;/p&gt;

&lt;h2&gt;
  
  
  The part I'd actually show a hiring manager
&lt;/h2&gt;

&lt;p&gt;Deployed-vs-trained drift is a real production failure mode, so the JS engine is tested against the Python model directly: ten fixture digits, expected probabilities exported from training, asserted in Node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;max prob diff vs Python: 1.14e-6
correct: 10/10
PARITY OK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If I change the inference code and break numerical equivalence, CI knows before a visitor does. That habit, &lt;em&gt;verifying the deployment artifact and not just the training run&lt;/em&gt;, is worth more than another accuracy point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this goes beyond MNIST
&lt;/h2&gt;

&lt;p&gt;The demo is intentionally the simplest possible instance, because the point is the deployment discipline around it, and that discipline scales to systems that matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Quantization with a budget.&lt;/strong&gt; Post-training int8 cost nothing here because the network is over-provisioned for the task. Real systems choose between quantization-aware training, mixed precision for sensitive layers, and distillation into a smaller student. The decision process stays identical: measure, compress, re-verify against parity fixtures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The browser is becoming a serious inference target.&lt;/strong&gt; WASM SIMD and WebGPU put surprisingly large models within reach of a static page. The interesting product class is anything where privacy is the feature: medical screeners, document processing, anything users would refuse to upload.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parity testing as a discipline.&lt;/strong&gt; The 1e-6 check between Python and JavaScript is a miniature of a production problem: proving the deployed artifact matches the trained one across runtimes, hardware, and compiler versions. Most teams discover deployment drift in production; fixtures catch it in CI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;From digits to your domain.&lt;/strong&gt; Swap the dataset and the same pipeline covers gesture commands, anomaly scoring over sensor windows, or keyword spotting: train anywhere, export weights, verify parity, run on-device. The 145 KB ceiling is a design constraint that forces good decisions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Try it&lt;/strong&gt; (draw badly, it copes): &lt;a href="https://rs-03.github.io/demos/#live-demo" rel="noopener noreferrer"&gt;rs-03.github.io/demos&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Source&lt;/strong&gt;: &lt;a href="https://github.com/rs-03/rs-03.github.io" rel="noopener noreferrer"&gt;github.com/rs-03/rs-03.github.io&lt;/a&gt;: training script, inference engine, and parity test.&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>python</category>
      <category>javascript</category>
      <category>neuralnetworks</category>
    </item>
    <item>
      <title>Testing Camouflage Against the Real Adversary: an AI</title>
      <dc:creator>Rahul Sangamker</dc:creator>
      <pubDate>Wed, 10 Jun 2026 21:49:52 +0000</pubDate>
      <link>https://dev.to/rahul_sangamker_653e0c1ba/testing-camouflage-against-the-real-adversary-an-ai-34f1</link>
      <guid>https://dev.to/rahul_sangamker_653e0c1ba/testing-camouflage-against-the-real-adversary-an-ai-34f1</guid>
      <description>&lt;p&gt;&lt;em&gt;Camouflage has always been graded by human eyes. But the thing hunting for you in 2026 is increasingly a detection model, so test against that.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The premise
&lt;/h2&gt;

&lt;p&gt;Surveillance is automated now: drones, trail cameras, perimeter systems. Most of what "sees" runs an object-detection network. Which makes traditional camouflage evaluation (a person squinting at a photo) the wrong test. The right test is adversarial: &lt;strong&gt;run the actual detector against your concealment and measure what it finds.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's the whole demo: upload a photo, and an object-detection model hunts for people in it at four simulated distances, producing a detection-range profile and a stealth score.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simulating distance with pixels
&lt;/h2&gt;

&lt;p&gt;You can't move the camera after the photo is taken, but you can simulate the dominant factor in long-range detection: &lt;strong&gt;pixels on target&lt;/strong&gt;. A person at 50 m simply occupies far fewer pixels than at 5 m. So each analysis run downscales the image progressively and re-runs detection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DISTANCE_LEVELS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Close (~5m)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="na"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Mid (~15m)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="na"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.45&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Far (~30m)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="na"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.22&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Very far (~50m)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.12&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;DISTANCE_LEVELS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scaled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;drawScaled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;detections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scaled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// best 'person' confidence at this simulated range&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output reads like a range card: &lt;em&gt;detected at 5 m with 96% confidence, 41% at 15 m, invisible beyond 30 m.&lt;/em&gt; A stealth score aggregates it: how poorly did the adversary see you, averaged across ranges?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyft1weyb1blv6a6acujr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyft1weyb1blv6a6acujr.png" alt="Distance simulation: the same photo downscaled four times and re-run through the detector" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest about the model
&lt;/h2&gt;

&lt;p&gt;The detector is COCO-SSD (a pretrained MobileNet-based model from the TensorFlow.js team) running entirely on-device. I didn't train it, and the demo says so on the page. The contribution here is the &lt;em&gt;evaluation framework&lt;/em&gt;: using detectors as adversaries, simulating range, and turning subjective "good camo" into a measurable profile. The full version of this concept goes further: a multi-model ensemble (YOLO, Faster R-CNN, RetinaNet), lighting variation, and heatmaps showing &lt;em&gt;which region of you&lt;/em&gt; gave you away.&lt;/p&gt;

&lt;h2&gt;
  
  
  From toy to instrument
&lt;/h2&gt;

&lt;p&gt;A single detector at four synthetic distances is the honest minimum that demonstrates the idea. Turning it into a real evaluation instrument is mostly known engineering plus a few open questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ensembles disagree, and that is the point.&lt;/strong&gt; YOLO-family, R-CNN-family, and transformer-based detectors fail differently. Concealment that defeats one family often fails against another, so a credible score must aggregate across architectures, the way a security audit uses multiple scanners.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explanation, not just detection.&lt;/strong&gt; The useful output for a designer is which region gave you away. Saliency maps over detector activations turn pass/fail into actionable feedback: break up the shoulder line; the head silhouette is carrying the detection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Condition sweeps.&lt;/strong&gt; Honest evaluation varies illumination, weather, motion blur, and sensor type. Thermal is the hard one: visible-spectrum camouflage does nothing against IR, and simulating thermal signatures faithfully is an open problem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The moving-target problem.&lt;/strong&gt; Detectors improve every year, so concealment effectiveness is a date-stamped claim. A serious evaluation service would re-test against current models continuously, like dependency scanning for the physical world.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The research framing: this is adversarial robustness studied from the defender's side of the camera, and the literature on physical adversarial attacks maps onto it almost one-to-one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this framing matters beyond camouflage
&lt;/h2&gt;

&lt;p&gt;"Evaluate against the deployed adversary, not a human proxy" generalizes: testing ad creatives against content classifiers, validating anonymization against re-identification models, red-teaming computer vision systems before someone else does. Adversarial evaluation is a product category hiding inside a security habit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it&lt;/strong&gt; (images analyzed on-device, nothing uploaded): &lt;a href="https://rs-03.github.io/demos/#camouflage" rel="noopener noreferrer"&gt;rs-03.github.io/demos&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Source&lt;/strong&gt;: &lt;a href="https://github.com/rs-03/rs-03.github.io" rel="noopener noreferrer"&gt;github.com/rs-03/rs-03.github.io&lt;/a&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>computervision</category>
      <category>security</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Mirror Therapy Without the Mirror Box: Treating Phantom Limbs in a Browser Tab</title>
      <dc:creator>Rahul Sangamker</dc:creator>
      <pubDate>Wed, 10 Jun 2026 21:49:49 +0000</pubDate>
      <link>https://dev.to/rahul_sangamker_653e0c1ba/mirror-therapy-without-the-mirror-box-treating-phantom-limbs-in-a-browser-tab-5750</link>
      <guid>https://dev.to/rahul_sangamker_653e0c1ba/mirror-therapy-without-the-mirror-box-treating-phantom-limbs-in-a-browser-tab-5750</guid>
      <description>&lt;p&gt;&lt;em&gt;A 1990s Nobel-adjacent therapy, a webcam, and 21 hand keypoints, recreating the mirror-box illusion for phantom limb pain, no hardware required.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A therapy built on an illusion
&lt;/h2&gt;

&lt;p&gt;In the 1990s, neuroscientist V.S. Ramachandran discovered something remarkable: amputees suffering phantom limb pain often felt relief just by &lt;em&gt;seeing&lt;/em&gt; their missing limb move again. His apparatus was almost comically simple: a box with a mirror. Put your intact hand in, look at its reflection where the missing hand would be, and move. The brain, watching the "missing" hand obey commands again, often dials the pain down.&lt;/p&gt;

&lt;p&gt;The limitation was never the science. It was the box: a physical apparatus, used in clinics, hard to scale, impossible to measure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Replacing glass with keypoints
&lt;/h2&gt;

&lt;p&gt;A webcam plus real-time hand tracking can produce the same illusion with better properties:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;webcam frame → hand landmark model (21 keypoints, on-device)
→ reflect: phantom[i] = { x: 1 − x, y, z }
→ render real hand (solid) + phantom twin (ghost) on canvas
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reflection is one line of math. Everything around it is what makes the illusion land:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;phantom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;real&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;z&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdwwomsymcajcsa07nph6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdwwomsymcajcsa07nph6.png" alt="The mirror-box illusion digitized: webcam, hand landmarks, reflection, rendered phantom twin"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The visual treatment matters more than I expected. The phantom hand is rendered as a ghostly cyan skeleton with a translucent palm fill, a "breathing" glow that pulses on a ~3 second cycle, and a fading afterimage trail of its last few frames. It reads as &lt;em&gt;present but ethereal&lt;/em&gt;, which is exactly the perceptual story mirror therapy needs to tell. A dashed mirror plane down the center of the frame makes the reflection relationship legible at a glance.&lt;/p&gt;

&lt;h2&gt;
  
  
  The engineering details that matter
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tracking&lt;/strong&gt;: MediaPipe HandLandmarker (Google's pretrained model, credit where due), running via WebAssembly with GPU delegate. ~30 FPS on a laptop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacy by architecture&lt;/strong&gt;: every frame is processed on-device. For a &lt;em&gt;medical-adjacent&lt;/em&gt; application, "video never leaves your browser" isn't a feature, it's a requirement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lazy loading&lt;/strong&gt;: the model only downloads when the user clicks "Start the Mirror", so visitors who don't engage pay zero bandwidth.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lifecycle hygiene&lt;/strong&gt;: camera tracks stopped and the model closed on unmount; nothing leaks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why digitize a working therapy?
&lt;/h2&gt;

&lt;p&gt;Because software adds what glass can't: guided exercise sequences, session tracking, range-of-motion measurement over time (the keypoints are already numbers), and remote delivery to patients who will never visit a clinic with a mirror box. The browser preview demonstrates the core interaction; a WebXR version with a fully rendered 3D limb is the natural next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  The research questions worth chasing
&lt;/h2&gt;

&lt;p&gt;The browser preview proves the illusion mechanism. The interesting work starts after that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Embodiment dosage.&lt;/strong&gt; The research suggests the sense of ownership over the virtual limb drives relief. What level of visual fidelity does that require? A glowing skeleton, a stylized hand, or a photorealistic limb with matched skin tone? That is a testable dose-response question, and a browser-based system can run it at a scale no clinic can.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Therapy that measures itself.&lt;/strong&gt; A physical mirror box produces zero data. Hand tracking produces 21 trajectories per frame, so range of motion, movement smoothness, and session adherence can be measured and trended across weeks. A therapy that quantifies its own effect can prove, or disprove, that it works for a given patient.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adaptive difficulty.&lt;/strong&gt; Pain-gated progression: exercises that advance only when movement quality and self-reported pain allow, which is how good physical therapists already operate. Encoding that judgment is a product problem, not a research one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lower limbs.&lt;/strong&gt; Hands are the easy case. Phantom leg pain is more common after amputation, and full-body pose models make a seated mirror-leg version plausible with the same architecture.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this needs new ML. It needs careful product and clinical work on top of commodity tracking, which is exactly the engineering that turns a demo into a deployable therapy tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it&lt;/strong&gt; (camera optional, on-device only): &lt;a href="https://rs-03.github.io/demos/#mirror" rel="noopener noreferrer"&gt;rs-03.github.io/demos&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Source&lt;/strong&gt;: &lt;a href="https://github.com/rs-03/rs-03.github.io" rel="noopener noreferrer"&gt;github.com/rs-03/rs-03.github.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A demonstration of the interaction concept, not a medical device and not medical advice.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>computervision</category>
      <category>webdev</category>
      <category>healthtech</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Your Cough Has a Fingerprint: Hand-Rolling an FFT and MFCCs in JavaScript</title>
      <dc:creator>Rahul Sangamker</dc:creator>
      <pubDate>Wed, 10 Jun 2026 21:49:05 +0000</pubDate>
      <link>https://dev.to/rahul_sangamker_653e0c1ba/your-cough-has-a-fingerprint-hand-rolling-an-fft-and-mfccs-in-javascript-e4k</link>
      <guid>https://dev.to/rahul_sangamker_653e0c1ba/your-cough-has-a-fingerprint-hand-rolling-an-fft-and-mfccs-in-javascript-e4k</guid>
      <description>&lt;p&gt;&lt;em&gt;I built a personal cough-health monitor that runs entirely in the browser: no ML framework, no server, no audio ever leaving your device. Here's how, down to the math.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea: deviation, not classification
&lt;/h2&gt;

&lt;p&gt;Most "AI cough detection" projects train a classifier on a population dataset: thousands of strangers' coughs, labeled sick or healthy. That approach has a fundamental problem: &lt;em&gt;your&lt;/em&gt; healthy cough might sound like someone else's sick one.&lt;/p&gt;

&lt;p&gt;So I inverted it. Record your own healthy cough a few times to establish a &lt;strong&gt;personal acoustic baseline&lt;/strong&gt;. Later, the system answers a much easier question: &lt;em&gt;how different does your cough sound from your own baseline?&lt;/em&gt; No training data needed. No model. Just signal processing, which means it can run anywhere, instantly, privately.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pipeline
&lt;/h2&gt;

&lt;p&gt;Every cough becomes a 24-dimensional acoustic fingerprint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;microphone → trim to cough peak → frame (Hamming window)
→ FFT → mel filterbank (26 filters) → log → DCT
→ 12 MFCCs → mean + std across frames = fingerprint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the classic MFCC (mel-frequency cepstral coefficients) front-end used in speech recognition for decades, but implemented from scratch in ~200 lines of JavaScript, because shipping TensorFlow.js for what is fundamentally an FFT felt absurd.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbuqav88lmx6wyihtaoit.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbuqav88lmx6wyihtaoit.png" alt=" " width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The FFT, in 40 lines
&lt;/h2&gt;

&lt;p&gt;The heart is an iterative radi&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs61sgjg1ntj716kavalp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs61sgjg1ntj716kavalp.png" alt=" " width="800" height="427"&gt;&lt;/a&gt;x-2 Cooley–Tukey FFT:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;len&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;len&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;len&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wRe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;wIm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;len&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;curRe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;curIm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;len&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// butterfly: combine even/odd halves&lt;/span&gt;
            &lt;span class="p"&gt;...&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mel filters then pool FFT bins the way human hearing does (finer resolution at low frequencies, coarser at high), and a DCT decorrelates the log energies into compact coefficients. Two coughs are compared by cosine similarity between their fingerprints.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify, don't vibe
&lt;/h2&gt;

&lt;p&gt;Hand-rolled DSP is exactly the kind of code that &lt;em&gt;looks&lt;/em&gt; right and is subtly wrong. So the whole pipeline is pure functions, unit-tested in Node before it ever touched a browser:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;FFT output vs. a naive O(n²) DFT reference: &lt;strong&gt;max difference 1e-14&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Identical signal vs. itself: &lt;strong&gt;similarity 1.000&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Same synthetic "cough" with added noise and 30% volume change: &lt;strong&gt;0.998&lt;/strong&gt; (volume invariance matters; you won't cough at calibrated loudness)&lt;/li&gt;
&lt;li&gt;Spectrally different burst: &lt;strong&gt;~0.0&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last pair is the whole product: robust to irrelevant variation, sensitive to spectral change.&lt;/p&gt;

&lt;h2&gt;
  
  
  The demo is the floor, not the ceiling
&lt;/h2&gt;

&lt;p&gt;The live demo is deliberately minimal: three baseline coughs, one comparison, one verdict. That is the smallest version that proves the core mechanism honestly, and it runs in thirty seconds on any device. The concept underneath is a research program:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Adaptive baselines.&lt;/strong&gt; A fixed baseline ages; respiratory health drifts with seasons, allergies, and habits. The next step is a slowly adapting baseline (exponentially weighted, with change-point detection) that distinguishes "your cough evolved gradually" from "your cough changed overnight", which is the clinically interesting event.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trajectories, not snapshots.&lt;/strong&gt; A single deviation score is weak evidence. A two-week trend of rising deviation is a different signal entirely, and it is the one worth showing a doctor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confound separation.&lt;/strong&gt; The honest open problem: distinguishing illness-driven spectral change from microphone distance, background noise, and time of day. Promising attacks include recording-condition normalization, per-session calibration sounds, and paired healthy/sick data per person.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Beyond coughs.&lt;/strong&gt; Personal-baseline deviation generalizes to any repeated personal sound: voice fatigue for call-center workers and singers, breathing during sleep, machine acoustics on a factory floor. The pattern (baseline, deviation, trend) is the product; the cough is one instance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is also a real research gap here: population models dominate the audio-health literature largely because labeled population data exists. Personalized baseline approaches have almost no shared benchmarks. Building one, using anonymized fingerprints rather than raw audio, would be a genuine contribution.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell a client
&lt;/h2&gt;

&lt;p&gt;This pattern, &lt;em&gt;personal baseline + deviation scoring instead of population classification&lt;/em&gt;, applies way beyond coughs: machine vibration monitoring, voice fatigue, equipment acoustics. It's cheaper than collecting a labeled dataset, inherently personalized, and privacy-preserving by construction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it live&lt;/strong&gt; (your audio never leaves the page): &lt;a href="https://rs-03.github.io/demos/#cough" rel="noopener noreferrer"&gt;rs-03.github.io/demos&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Source&lt;/strong&gt;: &lt;a href="https://github.com/rs-03/rs-03.github.io" rel="noopener noreferrer"&gt;github.com/rs-03/rs-03.github.io&lt;/a&gt;. See &lt;code&gt;dsp.js&lt;/code&gt; and its parity test.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Not a medical device; a demonstration of the signal-processing concept.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>machinelearning</category>
      <category>webdev</category>
      <category>audio</category>
    </item>
  </channel>
</rss>
