
I built an AI to recover the black plastic recyclers throw away. The classifier was the easy part.
Carbon black absorbs across the entire near-infrared, so a black polypropylene tray riding a black conveyor belt is a zero-signal void to every optical NIR sorter on the market, the TOMRA Autosort and Machinex and Pellenc class of machine that runs the world's recycling lines. The sorter cannot see the object at all, so it falls through to residue and then to landfill. Depending on the stream, 3 to 15 percent of the waste is black plastic lost exactly this way, and on a 50,000 tonne-per-year facility running 5 percent black that is roughly 2,500 tonnes a year of recoverable material, an opportunity on the order of 2.2 to 2.7 million euros annually (WP45 research, 2026). I spent a lot of this build assuming the interesting problem was making that plastic visible. It was, for about a week. Then the real problem showed up, and it was not the model at all.
The demo I built is called Assay, and it lives at https://veriprajna.com/demos/materials-recovery-ai. I should say what is real in it before I say anything else, because every demo in this space is tempted to blur the line. The spectra are synthetic, produced by a physics-grounded emulator standing in for a Specim FX50 sensor, and the pneumatic ejector is a logged stub. What is real is the classifier, the gates, the policy check, and the eval numbers. I built the synthetic side deliberately, because the case that decides whether a bale sells is the ambiguous, contaminated, PVC-in-the-stream case that a clean lab capture almost never hands you.
The week I spent trying to win the wrong race
I opened this build the way I think most people would, treating it as an accuracy race. The framing everyone brings to black-plastic recovery is a number: get the classifier from the NIR-blind zero all the way up to the lab's headline of nearly 99 percent, and you have solved recycling's blind spot. So I built the sensing physics first. Carbon black does dim the mid-wave infrared signal, but it does not erase it, and that gap is the entire opening. In the MWIR band, from 2.7 to 5.3 micrometers, each polymer still leaves a diagnostic fingerprint: the aliphatic carbon-hydrogen doublet for PP and PE, the aromatic ring for PS, the nitrile stretch for ABS, the chlorine-adjacent feature for PVC. I hand-rolled a small 1D convolutional network in numpy, three convolution layers into pooling into two dense layers into a softmax over PP, PE, PS, ABS, and PVC, with real forward and backprop and Adam and a temperature calibration step, and it runs as plain numerical code in a roughly 5 millisecond hot path. No large language model decides anything here. It is arithmetic you can read.
And it worked, at an honest level. On a held-out heavier stream of 1,700 objects, deliberately dirtier than what I trained on, it lands at 0.894 balanced accuracy, sitting right next to the 83.4 percent peer-reviewed field benchmark for MWIR classification on real waste (Resources, Conservation & Recycling, January 2026), with an interpretable peak-ratio rule at 0.668 underneath it so the network is not grading its own homework. That is a good number on a hard distribution. It is not the lab's 99 percent, and I made a decision early that I would never let it pretend to be.
Here is what stopped me. I sat with 0.894 and asked what a plant would do with it, and the answer was nothing. A materials recovery facility never buys the accuracy number. The only thing it pays for is a bale that clears the spec.
The toggle that reorganized the whole build
The moment the build turned was a toggle I added almost as an afterthought. The APR Model Bale Spec for Grade A recycled polypropylene is concrete and unforgiving: at least 97 percent PP and no more than 0.5 percent PVC. A bale that misses the spec is a rejected load, a landfill redirect fee, and a downstream recycler who now trusts you less. So I wired a single control onto the dashboard, a confidence gate you can switch on or off, and ran the identical model on the identical stream both ways to watch what the gate alone was worth.
With the gate on, low-confidence objects, the multilayer laminates and contaminated fragments whose spectra match no single polymer, get held out to manual QC at a softmax threshold of 0.86 instead of being guessed into the bale, and any PVC-suspect object above a deliberately low threshold gets hard-rejected outright. On demo seed 11 that gated bale measures 99.2 percent PP with zero PVC leaking through, and the certificate reads PASS, APR Grade A.

Then I flipped the same toggle off, so the classifier just takes its top guess on every object, naive argmax, nothing held back. Same stream. Same weights. The bale now reads 94.2 percent PP with 1.94 percent PVC, and the certificate flips to rejected, over the 0.5 percent PVC limit that defines the grade.

I had been building a classifier. What I was looking at was proof that the classifier was never the thing that made a bale sellable. The abstain, the PVC hard-reject, and the deterministic policy check were. The model did not change between those two screenshots. The guarantee infrastructure around it did, and that was the difference between a filable Grade A bale and a load of contaminated regrind.
Why I made PVC an asymmetric decision
I treated PVC differently from everything else on purpose, and the reasoning is worth being plain about. A PVC false negative in a PP bale is not a small error, it is a chlorine contaminant that can degrade an entire downstream melt. So the gate does not weigh PVC symmetrically against the other classes. Any object the network flags as even weakly PVC-suspect gets hard-rejected as a chlorine risk, which means I accept throwing away some genuine PP to make sure PVC does not ride along. That is a cost, and I wanted the demo to show the cost rather than hide it. Across a 60-bale fitted-gate evaluation the system routes about 12.6 percent of objects to manual QC and captures about 99.8 percent of the PVC, with a single PVC leak across all 60 bales, while recovering about 71.2 percent of the PP stream. It routes more material to a human than a naive system would, and it recovers less than a perfect one would, and it says so out loud. A recovery system that oversells its own purity has already failed at the one job it exists to do.

The trap in this whole idea is that abstaining looks like weakness. When I show engineers the manual-QC pile, the reflex is to ask why I do not just push the threshold and recover more. Because the thing being decided at a sort line is not how good the average guess is. It is whether a chlorine bomb enters a Grade A bale, and you do not want a stochastic model making that call. You want a rule the model cannot override.
The number I chose to put on the scoreboard
I made one call on the scoreboard number that cost me something to keep. 0.894 is the number that is true on a dirty held-out stream. Nearly 99 percent is the number that sells, the one from a clean Specim lab capture, and I could have quoted it and let people assume. I built the benchmark panel into the demo instead, sitting our 0.894 right beside the 0.668 interpretable baseline and the 83.4 percent peer-reviewed field figure, so anyone can see exactly where we stand and that we are not claiming the lab ceiling.

The panel carries the number that actually matters, and it is not the accuracy. Across 40 random heavy-stream bales, the gated bale meets APR Grade A about 85 percent of the time, while the ungated bale meets it only about 22.5 percent of the time. Read those two proof points carefully, because they are different measurements and I keep them separate on purpose: the 99.2-versus-94.2 comparison is one specific bale on demo seed 11, and the 85-versus-22.5 is the aggregate across 40 random bales. The single bale is the vivid version, the aggregate is the honest version, and the story only holds because both point the same way. A model at 0.894 becomes a bale that passes most of the time, or a bale that fails most of the time, depending entirely on whether the gate is in the loop.
What I stopped upselling
I built the compute side to argue against my own invoice, and that surprised me more than it should have. The latency math is simple: the displacement of the air burst equals the belt speed times the detection-to-fire latency, and the architecture is sufficient only when that burst spans no more than about four ejector-nozzle pitches, narrow enough to hold purity. At the facility's 3.0 meters per second, an optimized edge GPU (a Jetson AGX Orin running TensorRT at 14 milliseconds) produces a 42 millimeter burst spanning roughly 3.4 nozzle pitches, inside the window. So the engine recommends the GPU and states plainly that FPGA is not justified at this belt speed, saving about 32,000 euros and 20 weeks of engineering. Push the belt past roughly 3.6 meters per second and the GPU burst crosses the four-nozzle line, and only then does it flip to recommending an FPGA. It recommends the cheaper hardware whenever the physics allows, even though the FPGA is the bigger bill. I did not want a demo that nudged every buyer toward the expensive option, so I made the cheaper recommendation the default behavior and put the reasoning on screen.
The part that survives a better model
I did not build a sorter, and I want to be careful about that, because it is easy to hear this as a claim to replace TOMRA or Steinert or Machinex. That is the wrong reading. Assay sits downstream of your primary NIR sort as a vendor-neutral side-belt station, reading the black-heavy residue that sort already rejected, and it leaves the machine in front of it alone. And the value it adds is not a number that ages out as models improve. Every bale ships a signed certificate, hashed with sha256, stating its measured composition, each APR clause checked with a pass or fail, the model's hash, the abstain and reject counts, and the recovered tonnage. That receipt is the thing a plant can file.
It matters more now than it would have five years ago, for a specific regulatory reason. As the EU's PPWR (Regulation 2025/40) and California's SB 54 and the RecyClass framework take hold, carbon-black packaging counts as recyclable only where MWIR-capable sorting actually exists at the facility (WP45 research, 2026). That is not a compliance claim about this demo, the certificate is filable purity evidence, not a conformity stamp. It is context for why the ground is shifting. In that world the facility that wins is the one that can hand an auditor a filable receipt for every bale it ships, not the one with the highest benchmark on a slide. Trust at a sort line has to be deterministic, and the deterministic part is exactly the part that does not live inside the classifier.
And if you would rather watch a good model ship a failing bale than read me describe it, here is the whole thing running end to end.
If you want to watch a good model ship a failing bale, then flip one toggle and watch it ship a passing one, the demo is at https://veriprajna.com/demos/materials-recovery-ai, running the same stream both ways so the gate is the only thing that changes. I started this thinking the hard problem was seeing the black plastic. Seeing it took a week of spectroscopy and a small network. Proving that what I recovered was actually clean, and being willing to abstain, reject, and fail loudly when it was not, took everything after that.


