Why every neuroscientist should learn to code (and why every ML engineer should learn some neuroscience)

Here is a fact about your brain that most machine learning engineers have never heard, and most neuroscientists have never checked for themselves with code. Load a recording of 734 neurons from a mouse brain, count how often each one fires, and you find that the typical neuron produces about two spikes per second. A third of them fire less than once a second. The network that produces thought, movement and memory is, most of the time, nearly silent.

import numpy as np

data = np.load("steinmetz_session.npz", allow_pickle=True)
spike_times = data["spike_times"]          # 734 neurons, 45 minutes, one mouse

duration = max(st.max() for st in spike_times)
rates = np.array([len(st) / duration for st in spike_times])

print(f"median firing rate: {np.median(rates):.1f} spikes per second")
print(f"neurons below 1 Hz: {np.mean(rates < 1):.0%}")
# median firing rate: 1.9 spikes per second
# neurons below 1 Hz: 33%

Eight lines. Real data, freely available, from a paper published in Nature. Compare that with an artificial neural network, where every unit in every layer is computed on every forward pass, whether it has anything to say or not. The brain runs on roughly the power of a dim light bulb. A large language model needs a building full of hardware. The difference is not a detail. It is one of the central open problems in computing, and you can see the first clue to it in eight lines of Python.

That is what this site is about.

Two tribes that should be one

Modern AI began as an attempt to copy the brain. The perceptron, the neural network, the convolutional layer inspired by the visual cortex, reinforcement learning built on the dopamine literature: the lineage is direct. Then the two fields drifted apart. Machine learning became an engineering discipline organised around benchmarks and GPUs. Neuroscience became a data science organised around ever larger recordings, without always having the people to analyse them.

The result is two groups of very smart people who rarely read each other’s papers. Most ML engineers have never looked at a spike train. Many neuroscientists still hand their data to a collaborator, or to a graduate student who learned Python last month, because writing the analysis themselves feels like someone else’s job.

I have worked on both sides of this line. I currently work on precision medicine, brain-computer interfaces and semantic search, which means I spend my days translating between the two tribes. This site is the translation guide I wish had existed when I started.

This is not a new struggle. Every generation of scientists has been limited by the cognitive workload of its own tools. Johannes Kepler spent years of hand arithmetic wrestling the orbit of Mars into an ellipse, and later called it his war with Mars. For most of the last two centuries, the word computer meant a person: rooms of people, very often women, grinding through calculations for astronomers, engineers and, eventually, the first neuroscientists.

The clearest example in our own field is from 1952. Alan Hodgkin and Andrew Huxley had worked out the equations that describe how a nerve impulse travels along an axon, work that later won them a Nobel Prize. The Cambridge computer was out of service, so Huxley solved the equations by hand on a mechanical desk calculator. Computing a single propagated action potential took him about three weeks. On the laptop I am writing this on, the same calculation takes a few milliseconds.

The lesson is not that we are smarter than Huxley. It is that the bottleneck of science has always been computation, and the people who could do the computation decided what got discovered. Learning to program is how you stop being on the wrong side of that bottleneck.

Why every neuroscientist should learn to code

The data outgrew the spreadsheet a decade ago

A single Neuropixels probe records from hundreds of neurons at thirty thousand samples per second. A modern imaging session produces terabytes. An EEG study has dozens of channels, hundreds of trials, tens of participants. You cannot look at this data. You can only compute on it. The person who can write the computation controls what questions get asked.

Reproducibility is a property of code, not of intentions

A result that lives in a chain of clicks in a GUI cannot be repeated exactly, not even by you, six months later. A result that lives in a script can be re-run, reviewed, shared, and corrected. The reproducibility crisis in the life sciences has many causes, but one of the fixable ones is that analysis is still often done by hand.

The models are code

Every serious theory of how neurons compute is now expressed as a simulation. Hodgkin and Huxley wrote their model as differential equations in 1952 and solved them by hand-cranked calculator. Today the same model is thirty lines of Python. If you cannot read those lines, you cannot really engage with the theory. If you can write them, you can test it against your own data.

Why every ML engineer should learn some neuroscience

The brain is an existence proof

Every time someone claims that intelligence needs a trillion parameters, or a datacentre, or a training set of the entire internet, the brain quietly disagrees. It learns language from a few million words, not a few trillion. It learns to recognise an object from a handful of examples. It does this on twenty watts. We do not know how, and that is exactly the point: the gap between what brains do and what our models do is a map of the ideas we have not had yet.

Sparsity, timing and learning without backpropagation

Those nearly silent neurons at the top of this article are sparse coding in action. Real neurons also communicate with precisely timed events, not continuous activations, which is why neuromorphic chips exist. And no one has found backpropagation in the brain. Synapses change based on information that is local to them, in time and in space. Predictive coding, Hebbian learning, and dendritic computation are all attempts to explain how a network can learn without a global error signal. Any of them could be the next big idea in machine learning. Some of them already are.

Neuroscience is where the interesting problems are

Decoding intention from motor cortex to drive a prosthetic arm. Predicting a seizure minutes before it happens. Restoring speech from neural activity. Finding which patients will respond to which treatment from a brain scan. These are machine learning problems with unusual data, brutal constraints and enormous stakes. They are also where a working engineer can still make a first-author contribution, because the field needs builders.

What this site is

A learning path, not a link collection. Every article uses real neural data and real code, and each track builds on the one before it:

  1. Foundations. Python for people who have never programmed, taught entirely through neuroscience examples. Your first program plots a real spike train.
  2. Signals. What EEG, spikes and fMRI actually measure, and the signal processing that turns raw voltage into something meaningful.
  3. Data. The open datasets and file standards of the field, and how to load, clean and share neural data reproducibly.
  4. Models. From a single leaky neuron to Hodgkin-Huxley to small spiking networks, each one built from scratch in code.
  5. Decoding and brain-computer interfaces. Classifying intention from neural signals, ending in a minimal working BCI loop.
  6. Neuroscience meets AI. Where the two fields overlap today: deep networks as models of cortex, predictive coding, neuromorphic hardware, and what language models do and do not tell us about the brain.
  7. Precision medicine. Neural biomarkers, clinical machine learning, and the many ways a good model can fail in a hospital.

You do not need a neuroscience degree. You do not need to know how to program. You need Python installed, or a browser tab open to a free notebook service, and about an hour a week.

Start here

The first lesson takes you from a downloaded file to a working model of a neuron in about thirty lines of Python, using the same recording as the eight lines above. It is the most important article on this site, because after it you will know whether this path is for you.

Lesson 1: Your first neuron in Python.

Santiago Ramón y Cajal, whose drawings of neurons gave neuroscience its founding image, spent his mornings at the microscope and his afternoons at the drawing board, because the only way to truly see the structure was to render it himself. He told young scientists that anyone can become the sculptor of their own brain. That is the spirit here. The drawing board is now a text editor, and rendering the brain yourself means writing the code.

If something on this site helps you, or if you find a mistake, I would like to hear about it. You can reach me on Twitter at @thinquanaut.

Deep Blue never learned anything: the real difference between AI and machine learning

In May 1997 a computer beat the world chess champion. In March 2016 a computer beat one of the world’s best Go players. Both made headlines as triumphs of artificial intelligence, and both were. But the two machines had almost nothing in common. Deep Blue did not learn to play chess. AlphaGo did almost nothing but learn. The difference between those two machines is the difference between artificial intelligence and machine learning, and once you see it you will notice it everywhere.

Two words with two histories

Artificial intelligence was named in 1955, in a proposal for a summer workshop at Dartmouth College written by John McCarthy and three colleagues. Their ambition was the whole thing: machines that use language, form concepts, solve problems reserved for humans, and improve themselves. AI is the goal. It says nothing about the method.

Machine learning was named four years later by Arthur Samuel, an IBM engineer who had written a checkers program that got better by playing against itself. He defined it as giving computers the ability to learn without being explicitly programmed. Machine learning is one method. It is a way of getting to AI, or to a lot of things that nobody would call AI, by letting a program adjust itself from examples instead of following rules written by a person.

So the relationship is simple. Machine learning is a subset of artificial intelligence. Everything that learns from data to do something intelligent-looking is AI. But a great deal of AI, historically most of it, never learned anything.

flowchart TB

AI[Artificial intelligence: the goal] --> R[Rule-based systems]

AI --> ML[Machine learning: learn from examples]

R --> R1[Deep Blue, 1997]

R --> R2[Expert systems, 1970s to 80s]

R --> R3[Route planning, chess openings, tax software]

ML --> S[Supervised: labelled examples]

ML --> U[Unsupervised: find structure]

ML --> RL[Reinforcement: reward and punishment]

S --> DL[Deep learning: learned features]

DL --> D1[AlphaGo, 2016]

DL --> D2[Speech, vision, language models]
AI is the ambition. Machine learning is one family of methods for reaching it, and deep learning is one branch of that family.

The rules era

For its first thirty years, AI mostly meant writing rules. The Logic Theorist of 1956 proved theorems by searching through logical steps. Chess programs searched millions of positions per second and scored them with evaluation functions that grandmasters helped design. Expert systems of the 1970s and 80s, such as MYCIN for diagnosing blood infections and XCON for configuring computer orders at Digital Equipment Corporation, encoded the judgement of human specialists as thousands of if-then rules. XCON reportedly saved the company tens of millions of dollars a year.

This was genuinely intelligent behaviour by any reasonable standard of the time, and none of it was learned. Deep Blue is the peak of this era. It won because it could search two hundred million positions a second and because its evaluation function had been tuned by hand over years. Show it a new game and it would have been helpless.

The rules era ran into a wall that its own practitioners named: the knowledge acquisition bottleneck. Every rule had to be extracted from a human expert and written down. Experts disagreed, could not articulate what they knew, and the rules interacted in ways nobody could predict. The systems were brittle, expensive to build, and impossible to maintain. By the late 1980s funding collapsed in what is now called the second AI winter.

The learning era

The alternative had been there since the beginning. Frank Rosenblatt’s perceptron of 1958 learned to classify simple images by adjusting weights from examples, and Samuel’s checkers program learned from self-play. But learning needed two things the rules approach did not: large amounts of data and large amounts of computation. Neither was available.

Three things changed. The backpropagation algorithm, popularised in 1986, made it practical to train networks with many layers. The internet produced data at a scale nobody had imagined. And graphics cards built for video games turned out to be ideal for the arithmetic that neural networks need. The moment usually cited as the turning point is 2012, when a deep network called AlexNet cut the error rate on the ImageNet image recognition benchmark by a margin that made the entire field switch approaches within about two years.

AlphaGo is the peak of this era so far. Go has too many positions to search the way Deep Blue searched chess, and no human could write an evaluation function for it. So AlphaGo learned one, first from thirty million positions from expert games, then from millions of games against itself. Its successor, AlphaZero, dropped the human games entirely and learned chess, Go and shogi from nothing but the rules, and beat the best rule-based chess engine in the world in a matter of hours.

Spot the difference in daily life

The distinction is not academic. It tells you how a system will fail, what it needs to work, and who is responsible when it is wrong.

TaskRule-based approachLearned approach
Filtering spamBlock messages containing certain words. Beaten within weeks by misspellings.Since about 2002, classifiers trained on millions of labelled messages. Adapts as spam changes.
NavigationShortest-path search, as in every GPS. No learning needed; the road graph is known.Arrival-time prediction, which is learned from traffic history.
Filing taxesThe tax code is rules. Tax software encodes them exactly. A learned system here would be a liability.Fraud detection on the returns, learned from past cases.
Reading handwritingAttempts to describe letter shapes as rules failed for decades.Networks trained on handwritten digits have read postal codes and bank cheques since the early 1990s.
Detecting spikes in a neural recordingA voltage threshold, chosen by the experimenter. Still the first step in most pipelines.Sorting the detected spikes into neurons, which is clustering, a form of unsupervised learning.

Notice that the rule-based column is not the losing side. Your GPS, your tax software and your compiler are all rules, and they should be. The learned column wins only where the rules cannot be written down, which is the subject of the next article.

Where neuroscience sits in this

The brain was the inspiration for the learning side from the start. Rosenblatt was a psychologist, and the perceptron was explicitly a model of how neurons might learn. The modern neural network keeps the name and the rough idea, a network of simple units whose connections strengthen with experience, and discards nearly everything else about biology. That gap is the subject of the Neuroscience meets AI track on this site: what the brain does that our learners do not, and which of those differences matter.

It also cuts the other way. Neuroscience today uses both columns of the table above. A threshold detector is a rule. A spike sorter is a learner. A decoder that turns motor cortex activity into cursor movement is a learner, but the safety limits around it are rules. Knowing which is which is part of being able to build and trust these systems.

Further reading: Nils Nilsson, The Quest for Artificial Intelligence, is the standard history and is free online. Arthur Samuel’s 1959 paper on checkers is short and still readable.

Could you write the rules down? A field guide to when a problem needs machine learning

In 1954 a team from IBM and Georgetown University demonstrated a computer translating Russian sentences into English. It handled about sixty sentences using six grammar rules and a vocabulary of 250 words. The press reported that machine translation would be solved within five years. Twelve years later a government committee, the ALPAC report of 1966, concluded that after all that funding, machine translation was slower, worse and more expensive than a human, and recommended the money stop. It did, for two decades.

The problem was not effort or talent. It was that the researchers had picked the wrong kind of solution for the problem. Translation looked like a rules problem. It is not one. Knowing the difference is one of the most useful judgements a programmer can make, and it is the subject of this article.

The one question

Before anything else, ask: could a competent person write down the rules? Not in principle, in practice, on paper, completely enough that a program following them would be correct.

If yes, write the program. Sorting a list, computing payroll, validating a form, routing a packet, rendering a web page: these are rules problems, and every one of them has a correct, testable, explainable solution that a person wrote. Using machine learning here is not just unnecessary. It replaces a correct program with an approximate one, and hides the logic where nobody can inspect it.

If no, ask why not. There are three common reasons, and only some of them point to machine learning.

flowchart TD

Q1{Can a person write the rules down completely?} -- yes --> P[Write a regular program]

Q1 -- no --> Q2{Why not?}

Q2 -- The answer is a search or optimisation --> S[Use an algorithm: search, dynamic programming, solvers]

Q2 -- Humans do it but cannot say how --> Q3{Do you have many examples with the right answers?}

Q2 -- The pattern keeps changing --> Q3

Q3 -- no --> G[Get data first, or use rules as a stopgap]

Q3 -- yes --> Q4{Is an occasional wrong answer acceptable?}

Q4 -- no --> H[Rules or human review around any model]

Q4 -- yes --> ML[Machine learning is the right tool]
The decision in one diagram. Most problems exit at the first or second box.

Reason 1: the rules exist, but the answer is a search

Finding the fastest route across a city, scheduling flights, packing a truck, playing chess. Nobody can write down the answer, but the rules of the problem are perfectly clear. These are algorithm problems, and computer science has spent seventy years on them: shortest-path search, dynamic programming, constraint solvers, linear programming. They give exact or provably near-optimal answers, and they do not need training data. Reaching for machine learning here is a common and expensive mistake.

Reason 2: humans do it easily but cannot say how

Recognising a face. Understanding a spoken sentence. Telling a cat from a dog. Reading a handwritten postal code. Deciding whether an email is spam. You do these effortlessly, and if asked to write down the rules you would fail, as decades of researchers did before you. This is the home territory of machine learning: the knowledge exists, in human judgement, and can be captured as examples even though it cannot be captured as rules.

Translation is here. The rules approach failed for forty years. In the early 1990s an IBM group tried something that sounded like giving up: instead of grammar, feed the computer millions of sentence pairs from the Canadian parliament, which publishes everything in English and French, and let it learn which words and phrases tend to correspond. It worked better than the rule systems almost immediately. Frederick Jelinek, who led IBM’s speech group, is remembered for the remark that every time he fired a linguist, the recogniser improved. Modern translation is the descendant of that decision.

Reason 3: the rules keep changing

Spam filters are the clean example. In the late 1990s, filters were lists of forbidden words. Spammers responded with misspellings and images within weeks, and every update to the rules was answered by an update to the spam. In 2002 Paul Graham published a short essay proposing a filter that learned from each user’s own mail, and within a few years every major email provider used a learned filter. The point was not that the learned filter was cleverer. It was that it could be retrained overnight, while the rule-writers could not keep up. Fraud detection, recommendation, and ad targeting are the same shape: the target moves, so the program has to move with it.

Two more questions before you commit

Do you have the examples?

Machine learning is a way of turning examples into a program. No examples, no program. The examples must be numerous, must include the correct answer, and must look like the cases the system will meet in use. A model trained on hospital scans from one machine can fail on scans from another. A model trained on last year’s customers can fail on this year’s. If you cannot get the data, you are not ready, and a rule-based stopgap that collects data while it runs is often the right first version.

Can you afford to be wrong sometimes?

A learned system is approximate by construction. It will be wrong on some fraction of inputs, and it will not tell you which. Where a wrong answer costs a little and can be corrected, as in a recommendation or a spam folder, that is fine. Where a wrong answer is expensive or irreversible, the model needs rules around it: hard limits, human review, or a fallback. A brain-computer interface that moves a wheelchair is a learned decoder wrapped in rule-based safety limits, and it should be.

Real problems, sorted

ProblemVerdictWhy
Calculate a mortgage paymentProgramThe formula is the rule.
Find the fastest driving routeAlgorithmRules are known; the answer is a search.
Predict how long the route will take at 5 pmMachine learningDepends on patterns in traffic history nobody can write down.
Detect that a credit card is being used fraudulentlyMachine learning, with rulesThe pattern changes constantly; a few hard rules catch the obvious cases.
Read the amount on a handwritten chequeMachine learningHumans can do it and cannot explain how. Solved by neural networks in the 1990s.
Compile a programProgramEvery rule is specified. Exactness is the whole point.
Decide which neuron produced each spike in a recordingMachine learning (clustering)No rules exist; the spike shapes have to be grouped from the data.
Flag a seizure in an EEG recordingMachine learning, with rulesThe signature is learnable; the alert logic around it should be explicit.
Schedule operating rooms in a hospitalAlgorithmA constraint problem with clear rules, not a pattern to learn.

A test you can run on your own problem

Try to write the program. Spend an hour, or a day, writing the rules as if you had to. One of three things happens:

  1. You finish, and you have a program. It is probably better than any model would have been.
  2. You realise the rules are clear but the answer is a search or an optimisation. Look for the algorithm before you look for the model.
  3. You keep adding exceptions, the exceptions need exceptions, and you can feel the rules turning to mush. That feeling is the signal. If you have examples, and being wrong sometimes is acceptable, this is a machine learning problem.

The researchers of 1954 never ran this test, because it was not yet possible to fail it honestly. You can. It takes an afternoon and it can save a year.

Further reading: the ALPAC report of 1966 is short and surprisingly readable. Paul Graham’s essay A Plan for Spam (2002) is the best single account of why learning beat rules for a moving target.

Machine learning inside the microscope: Nikon’s NIS.ai and the open tools behind it

This started life in 2020 as a one-line note pointing at a page on Nikon’s website. The page is still worth pointing at, because it is a clear example of something that has quietly happened across microscopy: the machine learning is now inside the microscope software, sold as a set of modules, and a great many neuroscience images are passing through convolutional networks before anyone looks at them. This is a short guide to what those modules do, what they are under the hood, which free tools do the same job, and the one thing you must check before trusting any of them. Updated October 2026.

What Nikon ships

NIS.ai is a family of add-ons for NIS-Elements, the acquisition and analysis software that drives Nikon microscopes. Each module is a neural network that takes an image in and gives an image, or a set of labelled regions, back. Some come pre-trained; the more interesting ones learn from your own data. Nikon’s page is the authority on current pricing and bundling, which I will not repeat here because it changes; at the time of writing Denoise.ai ships with the AR package and the rest are licensed separately, and all of them want a GPU.

NIS.ai moduleWhat it doesTrained onThe open-source equivalent
Denoise.aiRemoves shot noise from confocal imagesPre-trained by NikonNoise2Void, CARE
Clarify.aiRemoves out-of-focus blur from widefield fluorescencePre-trained by NikonDeconvolution; CARE
Enhance.aiRestores detail in under-exposed imagesYour own paired imagesCARE
Convert.aiPredicts one channel from another, e.g. a nuclear stain from brightfieldYour own paired imagesIn-silico labelling; CARE’s image-to-image mode
Segment.aiFinds structures that thresholding cannot, from hand-traced examplesYour own traced imagesCellpose, StarDist, ilastik

Two of these are the kind of thing you would expect a camera company to ship. Denoising and deblurring have a long history in microscopy, deconvolution in particular, and a network trained on a lot of Nikon images is a sensible modern replacement. The other three are more surprising, and more important.

What they are, underneath

Every NIS.ai module is a descendant of a handful of academic papers from 2015 to 2021, all of them open, most of them with code. Knowing the lineage tells you what the tool can and cannot do.

  • U-Net (Ronneberger, Fischer and Brox, 2015). A convolutional network shaped like a U: it shrinks the image to understand it, then grows it back to the original size to label every pixel. Written for biomedical segmentation, and still the backbone of nearly every image-to-image tool below, including, almost certainly, Nikon’s.
  • CARE, content-aware image restoration (Weigert and colleagues, 2018). Train a U-Net on pairs of bad and good images of the same sample (low light and high light, low resolution and high resolution) and it learns to turn bad into good. This is Enhance.ai’s recipe, and the CSBDeep package is the open version.
  • Noise2Void (Krull, Buchholz and Jug, 2019). Denoising without clean examples: the network learns to predict each pixel from its neighbours, which it can only do for the signal and not for the noise. Pre-trained denoisers like Denoise.ai are a cousin of this idea.
  • In-silico labelling (Christiansen and colleagues, 2018; Ounkomol and colleagues, 2018). Predict a fluorescent stain from a transmitted-light image, because the structure the stain reveals is faintly present in the unstained picture. This is Convert.ai, and it is the module most likely to make a biologist gasp and a statistician wince, for the same reason.
  • Cellpose (Stringer, Wang, Michaelos and Pachitariu, 2021) and StarDist. General-purpose cell and nucleus segmentation that works out of the box on most images and can be fine-tuned on yours in a GUI. Segment.ai does the same job inside NIS-Elements. Cellpose comes from the Janelia group whose data appears in the data page, and it is the tool I would reach for first.

The thing to check

An image-to-image network produces a plausible image. That is its job, and it is also the problem. A restored or converted image can contain structure that was never in the sample: a denoiser that has seen a lot of mitochondria will draw mitochondria into noise, and a channel-prediction network will confidently paint a nucleus where the training set says one usually is. Nothing in the output flags this. The people who wrote CARE say so plainly in the paper, and the rule they give is the right one:

  • Keep the raw images, always, and do your quantification on them or on something you can trace back to them.
  • Validate on data where you know the answer: hold out real pairs, as in lesson 3, and look at the worst cases, not the average.
  • Treat restored images as a way to see, and segmentation masks as a hypothesis to check, not as measurements.
  • When the method goes in a paper, say which network, which version, and what it was trained on. A reviewer cannot evaluate “AI denoising”.

If you want to try this without a Nikon licence

Everything in the table has a free equivalent that runs on a laptop, or in a browser, and that you can read the source of.

  • ZeroCostDL4Mic (von Chamier and colleagues, 2021): Google Colab notebooks that train and run CARE, Noise2Void, StarDist, Cellpose, U-Net and more on your own images, with no installation and a free GPU. The best first step.
  • Cellpose: pip install cellpose, open the GUI, drop in an image. Fine-tune on a few of your own corrected masks when the default model is not good enough.
  • napari: the Python image viewer that most of these tools plug into, and the place to look at your results slice by slice.
  • BioImage Model Zoo: pre-trained models for microscopy, in a format that runs in Fiji, ilastik, napari and DeepImageJ.
  • For calcium imaging specifically, Suite2p and CaImAn do registration, cell detection and spike inference end to end, and are what the labs producing the datasets on this site actually use.

The vendor tools earn their price by being there at the moment of acquisition, with support, inside the software the microscope already runs. The open tools earn their place by being inspectable, citable and free, which is what a result needs to be when it leaves the lab. Most imaging groups I know end up using both, and the ones who understand the lineage above use both well.

What is a brain-computer interface?

A brain-computer interface, or BCI, is a system that reads activity from the brain and turns it into an action in the outside world without going through muscles. Move a cursor by imagining your hand moving. Spell words by attending to letters on a screen. Steer a wheelchair. Control a robotic arm. The idea is simple to state and has taken half a century of neuroscience, signal processing and machine learning to make real.

This article is the map. It explains what a BCI is made of, how brain activity is measured, what signals are actually decoded, what the technology can and cannot do today, and how to start building one yourself. It is also the entry point to the Decoding and BCI track on this site, where each of these pieces gets its own hands-on lesson.

The loop

Every BCI, from a research prototype to an implanted clinical device, is the same closed loop with four stages:

  1. Measure. Record a signal that reflects brain activity: electrical potentials from the scalp, the cortical surface, or inside the tissue.
  2. Extract. Clean the recording and compute features that carry information: the power in a frequency band, the timing of a voltage deflection, the firing rate of a neuron.
  3. Decode. Map those features onto an intention. This is a machine learning problem: a classifier that says left or right, or a regression that outputs a cursor velocity.
  4. Act and feed back. Carry out the intention and show the user the result. The user sees the cursor move and adjusts. The brain learns the interface at the same time as the interface learns the brain.
flowchart LR

U((The brain)) --> M[1. Measure]

M --> X[2. Extract features]

X --> D[3. Decode intention]

D --> A[4. Act]

A -- feedback --> U
The closed loop. Every brain-computer interface is this diagram, whatever the hardware.

That last point is the thing people miss. A BCI is not a mind-reading device bolted onto a passive brain. It is a two-way learning system, and much of the practical progress in the field has come from making the feedback loop faster and the user’s job easier.

How the brain is measured

Recording methods trade off three things against each other: how much detail you get, how much of the brain you can see, and how invasive the method is. Nobody has all three.

MethodWhat it isCoverageDetailInvasiveness
EEGElectrodes on the scalpWhole headCoarse: millimetres of skull and skin blur the signalNone. Consumer headsets exist
ECoGElectrode grid on the cortical surfaceOne regionGood: fast, local signalsSurgery, usually during epilepsy monitoring
Intracortical arraysNeedle electrodes inside the cortexA few square millimetresBest: individual neuronsSurgery, tissue response over time
fNIRS and fMRIBlood oxygenation, via light or magnetismWhole brain (fMRI)Slow: seconds, not millisecondsNone, but fMRI needs a scanner
MEGMagnetic fields from neural currentsWhole headBetter than EEG, still indirectNone, but needs a shielded room

Most of the BCI research you can do at home uses EEG, because the hardware costs a few hundred dollars and the datasets are public. Most of the headline clinical results use intracortical arrays, because when you can hear individual neurons you can decode far more. The middle of the table is where a lot of current commercial effort sits.

What signals are actually decoded

The raw recording is voltage over time. The decodable information lives in a handful of patterns within it.

Rhythms

EEG has recognisable oscillations, named by frequency band. The bands are conventions, not sharp boundaries, but the usual definitions are:

  • Delta, 0.5 to 4 Hz. Dominant in deep sleep.
  • Theta, 4 to 8 Hz. Drowsiness, memory tasks, and in animals the hippocampal rhythm that organises spatial memory.
  • Alpha, 8 to 13 Hz. Relaxed wakefulness; strongest over visual cortex with the eyes closed.
  • Beta, 13 to 30 Hz. Active thinking and, importantly for BCI, motor control.
  • Gamma, above 30 Hz. Local cortical processing; hard to see from the scalp, clear in ECoG.

The classic motor-imagery BCI works on one fact about these rhythms: when you move a hand, or imagine moving it, the alpha and beta power over the opposite motor cortex drops. Detect which side dropped, and you know which hand the user imagined. Two classes, one feature, and a working interface.

Evoked responses

Show someone a sequence of flashes and their EEG contains a reliable positive bump about 300 milliseconds after the one they were waiting for. That is the P300, and it powers the oldest spelling interfaces: flash rows and columns of a letter grid, find the row and column that produced a P300, and you have the letter. A related trick, the steady-state visual evoked potential, uses targets flickering at different frequencies; whichever frequency shows up in the EEG is the one the user is looking at.

Spikes and local field potentials

Inside the cortex, the signal is the firing of individual neurons, exactly the spike trains you work with in the first Foundations lesson. Neurons in motor cortex fire in proportion to the direction and speed of an intended movement. Record enough of them, fit a model from firing rates to velocity, and you can drive a cursor or a robotic arm. This is how the best-known clinical demonstrations work.

Decoding is machine learning

Once features are extracted, the decoder is a standard supervised learning problem with unusual data. Linear discriminant analysis and support vector machines still do most of the work in EEG, because the data are small and noisy and simple models generalise better. Kalman filters have been the workhorse for cursor control from spikes for two decades, because they naturally smooth the noisy estimate over time. Deep networks have arrived for the hardest problems, such as decoding attempted speech from cortical recordings, where recent studies have produced text at close to conversational rates for people who had lost the ability to speak.

What makes BCI decoding hard is not the algorithm. It is that the signal changes: electrodes shift, the brain adapts, attention wanders, and a decoder trained this morning is worse by the afternoon. Handling that drift is one of the open engineering problems in the field.

What works today

  • Cochlear implants are the most successful neural interface ever built, in use by hundreds of thousands of people. They run in the other direction, computer to brain, but they prove the basic engineering.
  • Deep brain stimulation for Parkinson’s disease and other conditions is routine clinical practice, and newer devices adjust stimulation based on the recorded signal, which makes them closed-loop BCIs.
  • Intracortical cursor and typing control has allowed people with paralysis to communicate and use computers in research settings for years, and several companies are now running early human trials of implanted devices intended for everyday use.
  • Speech decoding from cortical recordings moved from proof of concept to conversational speed in the last few years.
  • Consumer EEG headsets can reliably detect a few coarse states such as eyes closed, drowsiness or attention to a flickering target. They cannot read thoughts, and the ones that claim to are selling the band names above, not neuroscience.

Why this matters for the rest of this site

A brain-computer interface is where every track on this site converges. You need to understand what the recording is measuring, which is the Signals track. You need to handle real data in standard formats, which is the Data track. You need a model of what neurons are doing, which is the Models track. And you need to build a decoder that works on a live stream and keeps working, which is the Decoding and BCI track itself. If you follow the path from the beginning, a working motor-imagery BCI on public EEG data is the destination.

How to start

You do not need hardware. Everything below is free.

  • Data. The PhysioNet EEG Motor Movement/Imagery dataset has over a hundred subjects doing exactly the left-versus-right task described above. The BNCI Horizon 2020 collection has dozens more BCI datasets in one place.
  • Tools. MNE-Python for loading, filtering and visualising EEG and MEG. scikit-learn for the decoder. MOABB for benchmarking BCI algorithms across datasets without rewriting the loading code each time.
  • Hardware, later. When you want to close the loop on your own head, open-source boards such as OpenBCI cost a few hundred dollars and stream directly into Python.
  • Reading. Wolpaw and Wolpaw’s Brain-Computer Interfaces: Principles and Practice is the standard reference and covers everything above in depth.

The author declares no conflict of interest.

Base64 to image: what it is, how to do it, and when you should

In 2018 I spent some time working out how to move medical images, TIFF files among them, between services and into web pages without a separate request for every picture. Base64 turned up everywhere: in data URIs, in JSON payloads, in the FHIR healthcare standard. This is the article I wish I had read first: what Base64 actually is, a worked example small enough to check by hand, working code in Python, Java and JavaScript, and an honest account of when inlining an image as text is a good idea and when it is not.

What Base64 is

Computers store everything as bytes, but a surprising number of the channels we push data through were designed for text: email, HTML, JSON, URLs, XML, configuration files. Hand them raw image bytes and something along the way may treat a byte as a line break, a control character, or the end of the message. Base64 solves this by rewriting any sequence of bytes using only 64 safe characters: A to Z, a to z, 0 to 9, + and /, with = as padding at the end.

The mechanism is simple. Eight bits per byte, six bits per Base64 character, so every three bytes become four characters. Here is the classic example, the word Man:

Input bytesMan
As numbers7797110
As 8-bit binary010011010110000101101110
Regrouped into 6 bits010011   010110   000101   101110
As numbers 0 to 6319   22   5   46
Looked up in the alphabetT   W   F   u

Man becomes TWFu. When the input is not a multiple of three bytes, the last group is padded with zero bits and one or two = signs mark how much padding there was: Ma becomes TWE= and M becomes TQ==. The cost is that four characters carry three bytes of information, so Base64 output is always about a third larger than the input. A 1.9 KB image becomes 2.5 KB of text.

Two things Base64 is not. It is not compression: the output is bigger, never smaller. And it is not encryption, even though it looks like gibberish and often appears next to cryptographic material. Anyone can turn a Base64 string back into the original bytes with one function call, so it protects nothing. It is a transport format, and that is all.

Image to text and back, in Python

The Python standard library has done this since the beginning. Open the file in binary mode, encode, and turn the resulting bytes into a string.

import base64

with open("brain.png", "rb") as f:          # "rb": read the raw bytes, not text
    raw = f.read()

text = base64.b64encode(raw).decode("ascii")   # bytes in, bytes out; .decode turns them into a str
print(len(raw), "bytes of PNG ->", len(text), "characters of Base64")
print(text[:60] + "...")
# 1904 bytes of PNG -> 2540 characters of Base64
# iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAADAFBMVEU7PT4g...

Every PNG file starts with the same eight bytes, so every Base64-encoded PNG starts with iVBORw0KGgo. Once you have seen that prefix a few times you will recognise an inlined PNG anywhere. JPEGs start with /9j/.

To put the image in a web page, prefix the text with a small header that says what it is. This is a data URI, and browsers accept it anywhere they accept a normal image address.

data_uri = "data:image/png;base64," + text
html = f'<img src="{data_uri}" alt="a brain">'
print(html[:80] + "...")
# <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAD...
The site icon, delivered as Base64 text inside this article
This image is not a file on the server. It is 2,540 characters of Base64 text sitting in the HTML of this article, and your browser decoded it.

Going the other way is one call to b64decode. The decoded bytes are a PNG file in memory; BytesIO lets Pillow open them as if they were on disk, and from there they are a NumPy array, which is where every image analysis on this site begins.

import io
import numpy as np
from PIL import Image

raw_again = base64.b64decode(text)
print("round trip identical:", raw_again == raw)

img = Image.open(io.BytesIO(raw_again))        # BytesIO makes bytes in memory look like a file
pixels = np.array(img.convert("RGB"))          # a NumPy array: rows x columns x (red, green, blue)
print(img.format, img.size, pixels.shape, pixels.dtype)
# round trip identical: True
# PNG (32, 32) (32, 32, 3) uint8

The same thing in Java and JavaScript

The original version of this article used the Apache Commons codec and got the last line wrong: Base64.encodeBase64(bytes).toString() returns the memory address of a byte array, not the encoded text, which is an easy mistake to make and a hard one to notice until something downstream fails to decode. Since Java 8 the standard library has a correct and simpler answer.

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;

// Java 8 or later: no third-party library needed
byte[] raw = Files.readAllBytes(Paths.get("brain.png"));
String text = Base64.getEncoder().encodeToString(raw);
byte[] rawAgain = Base64.getDecoder().decode(text);

In the browser the job is usually the reverse: a user picks a file and you want to show it, or send it to an API, without uploading it first. FileReader.readAsDataURL produces the complete data URI directly.

// In the browser: read a file the user picked, straight to a data URI
const file = document.querySelector("input[type=file]").files[0];
const reader = new FileReader();
reader.onload = () => { document.querySelector("img").src = reader.result; };
reader.readAsDataURL(file);   // result looks like "data:image/png;base64,iVBOR..."

When to inline an image, and when not to

The reason I went looking at this in the first place was web performance. Every image on a page used to cost a separate HTTP request, and in the HTTP/1.1 era, where a browser would open only a handful of connections to a server, a page with fifty small icons spent most of its load time waiting in line. Steve Souders built a career on documenting this, and the standard fixes were sprites (pack all the icons into one image and show a slice of it with CSS) and inlining (put the image in the page as a data URI, so it arrives with the HTML). Sprites work but are painful to maintain, and for large or unusual formats such as TIFF they do not work at all. Inlining looked like the way out.

It is, for some images. The honest trade-off in 2026:

  • Inline tiny images. Icons, logos, a one-pixel placeholder, anything under a couple of kilobytes. The 33 percent size penalty is a few hundred bytes, and saving a round trip is worth more than that.
  • Serve everything else as a file. A large inlined image bloats the HTML, cannot be cached separately from the page, cannot be lazy-loaded, and blocks the page from rendering until it has fully arrived. A file is fetched in parallel, cached once, and reused across pages.
  • HTTP/2 changed the maths. Requests are cheap now; many can share one connection. The case for sprites has mostly gone, and the case for inlining has shrunk to the very small.
  • APIs are the exception. When an image has to travel inside JSON or XML, Base64 is the only option, and the size penalty is the price of using a text format. This is why the FHIR standard for health data carries images and documents as Base64 in its Attachment and Binary resources, and why a great deal of medical imaging code is really Base64 plumbing.

Mistakes to watch for

  • Bytes versus strings. In Python 3, b64encode takes bytes and returns bytes. Forgetting the .decode() puts b'...' into your JSON. Forgetting to open the file with "rb" gives you a decoding error before you even start.
  • There is more than one alphabet. The URL-safe variant swaps + and / for - and _, because the standard characters have meanings inside a URL. Python has urlsafe_b64encode for it. Decoding one variant with the other fails.
  • Line breaks. Email-style encoders wrap the output every 76 characters. Most decoders tolerate the newlines; some do not. Strip them if in doubt.
  • Missing padding. Some systems drop the trailing = signs to save space. If a decoder complains about “incorrect padding”, append = until the length is a multiple of four.
  • Treating it as security. See above. If a system hides a password behind Base64, it is not hidden.

A note on image search

The title of the original article promised image search, and it is worth being clear about what Base64 contributes to that: nothing. The encoded text is a faithful copy of the bytes and carries no more meaning than they do. Searching for similar images means computing something from the pixels, a hash for exact duplicates or an embedding from a neural network for visual similarity, and comparing those. Base64 is just how the picture gets to the service that does the computing. That work, turning pixels into vectors you can compare, is a topic for the machine learning track of this site.

Featured

Algorithms

The term algorithm is used in computer science to describe a finite, deterministic, and effective problem-solving method suitable for implementation as a computer program. Algorithms are the stuff of computer science: they are central objects of study in the field.
We can define an algorithm by describing a procedure for solving a problem in a natural language, or by writing a computer program that implements the procedure.

Algorithms are not confined to mathematics alone. When you cook bread from a recipe, you’re following an algorithm. When you knit a sweater from a pattern, you’re following an algorithm. When you put a sharp edge on a piece of flint by executing a precise sequence of strikes with the end of an antler—a key step in making fine stone tools—you’re following an algorithm. Algorithms have been a part of human technology ever since the Stone Age.

THE STUDY OF ALGORITHMS AND DATA STRUCTURES is fundamental to any computer-science curriculum, but it is not just for programmers and computer-science students. Everyone who uses a computer wants it to run faster or to solve larger problems.

From N-body simulation problems in physics to genetic-sequencing problems in molecular biology, the algorithms have become essential in scientific research; from architectural modeling systems to aircraft simulation, they have become essential tools in engineering; and from database systems to internet search engines, they have become essential parts of modern software systems.

The descriptions of algorithms in this blog are based on complete implementations and on a discussion of the operations of these programs on a consistent set of examples. In addition to presenting pseudo-code, I like to work with real code, so that the programs can quickly be put to practical use. I tend ro write the code in Java, Python or both, but in a style such that most of our code can be reused to develop implementations in other modern programming languages.

As Carl Sagan put it, “Science is a way of thinking much more than it is a body of knowledge.”
Happy learning!

Here is a list of my favorite Open Data Sources that I use with my students often: