If you’ve ever been curious about the subtle hum of radioactivity in everyday items, you’re in the right place. Using a Radiacode scintillation detector, we’re taking a closer look at the slight radio emissions from objects like smoke detectors and vintage glass—a safe yet fascinating peek into atomic-scale interactions.
This third installment builds on our earlier work. The first part focused on analysing gamma spectroscopy data, while the second introduced a model for identifying isotopes. Now, we’re exploring two practical approaches: one through a freely hosted Streamlit app, and another using a more flexible Python HTMX-based app that talks directly with hardware. And if you don’t have Radiacode equipment, you can still experiment with datasets available on Kaggle.
Isotope Classification Model
The heart of our project is an isotope classification model built with XGBoost—a fast, efficient machine learning tool—and a label encoder. This model wraps everything in a neat Python class that loads the necessary files, processes the data, and makes a prediction. Check out the core snippet below:
from xgboost import XGBClassifier from sklearn.preprocessing import LabelEncoder class IsotopesClassificationModel: """Gamma Spectrum Classification Model""" def __init__(self): """Load models""" path = self._get_models_path() self._classifier = self._load_model(path + "/XGBClassifier.json") self._isotopes = self._load_isotopes(path + "/isotopes.json") self._labels_encoder = self._load_labels_encoder(path + "/LabelEncoder.npy") def predict(self, spectrum: Spectrum) -> str: """Predict the isotope""" features = SpectrumPreprocessing.convert_to_features(spectrum, self._isotopes) preds = self._classifier.predict([features]) preds = self._labels_encoder.inverse_transform(preds) return preds[0] @staticmethod def _load_model(filename: str) -> XGBClassifier: """Load model from file""" bst = XGBClassifier() bst.load_model(filename) return bst @staticmethod def _load_isotopes(filename: str) -> List: with open(filename, "r") as f_in: return json.load(f_in) @staticmethod def _load_labels_encoder(filename: str) -> LabelEncoder: le = LabelEncoder() le.classes_ = np.load(filename) return le @staticmethod def _get_models_path() -> str: """Get path to models stored in 'models/V1/' folder""" parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) return parent_dir + f"/models/{IsotopesClassificationModel.VERSION}"
The Spectrum class collects the data straight from the detector, and the SpectrumPreprocessing helper converts those readings into features that the model can analyse. For example, a 10‑minute capture of a slightly radioactive Chinese pendant produced a spectrum that the model neatly identified as thorium.
Streamlit makes it easy to build browser-based apps with minimal code. Its community cloud lets you share your prototypes quickly, although keep in mind the resource restrictions—just two cores and 2.7 GB RAM, along with temporary file storage. A simple Python script renders an HTML page with Matplotlib graphs, taking your local tests to a public stage.
If you’re after deeper interactivity and real-time data exchange, try the FastAPI and HTMX combo. FastAPI manages the server-side duties (including connecting with your Radiacode hardware), while HTMX keeps the web interface updated with live device status, radiation levels, and spectrum graphs. With periodic updates from Chart.js and seamless backend integration, this approach is ideal for those looking to push the envelope beyond prototypes.
During testing, our apps even picked up the subtle radioactivity of a blue apatite mineral—detected as thorium, an element not typically associated with apatite according to commonly available sources like Wikipedia and USGS. While this project isn’t geared toward commercial success, it has provided valuable hands-on experience with tools like FastAPI, HTMX, and machine learning for isotope detection.