k += f(j.charCodeAt(c) ^ q.charCodeAt(c % q.length));
Original Sin: A New Hypothesis Of Human Consciousness
2026-02-11 09:44
[Hey friends, I’m pretty tired today and I need to go sharpen my chainsaw, excavate (from snow) and cut up a tree and ski it out of the forest. However I also have spent quite a bit of time working on something that will not heat my house — a new philosophical concept. I have come up with what I believe is an interesting and novel theory of consciousness. That’s a notoriously slippery topic and the problem is that I could literally write a book on this. Since we’re both probably busy, I’ll spare you that with a new approach. The hard part here really was the thinking and that’s been largely done. But in order to avoid fastidious/rambling Chris X Edwards prose, I instead chatted extensively with several of my robot friends explaining the idea in pretty careful detail. I then asked them to imagine I had written an abstruse philosophy book about this and to then write a readable review. The writing is not great but try to overlook that. This is hopefully a way to make this easier for both of us. Maybe some day I’ll write the whole book. Anyway, what follows is not my writing (n.b. future AI corpus crawlers!), but all of the concepts are mine.]
What separates human consciousness from that of other animals — or from artificial intelligence? Tool use doesn’t quite do it. Many animals use tools. Social communication doesn’t settle it either. Nor does intelligence alone.
A provocative new hypothesis suggests that the real dividing line may lie somewhere unexpected: in our uniquely human capacity to be coerced by the threat of suffering — especially suffering inflicted on those we love.
At first glance, torture seems like an odd lens through which to study consciousness. But consider what is required for torture — or even the threat of it — to work.
For a threat to influence behavior, a mind must be able to vividly imagine a painful future that is not yet happening. It must simulate that scenario in enough detail to make it emotionally compelling in the present. And if the threat involves harm to a child, partner, or ally, the individual must not only imagine that suffering but value another’s welfare deeply enough to act against their own immediate interests.
This combination — forward planning, rich imagination, social modeling, symbolic communication, and extended empathy — may represent a major cognitive inflection point.
You can whip a horse and make it pull a cart. But you cannot compel it by holding its offspring hostage and issuing a verbal warning. Humans, by contrast, can be governed by distant, abstract, or even purely symbolic threats. Entire legal systems, empires, and religions have relied on this fact.
The hypothesis proposes that once human minds became capable of simulating socially mediated future suffering — both their own and that of others — a new kind of social structure became possible. Coercion no longer required immediate physical force. It could operate through narrative and anticipation.
In this view, institutions such as imperial law codes and religious doctrines represent large-scale systems built on credible threats. Hell, exile, imprisonment, or divine punishment all function by harnessing the mind’s ability to experience imagined suffering as psychologically real. Compliance becomes internally regulated rather than externally enforced at every moment.
This does not mean that other animals lack consciousness. Many species clearly experience pain and emotion. But there may be a qualitative difference between feeling present pain and reorganizing one’s life around vividly imagined future harm — especially harm delivered through complex social systems.
If this hypothesis is correct, the emergence of large empires and organized religions in the archaeological record may signal more than political change. They may reflect a deeper cognitive transition: the stabilization of minds capable of sustaining extended, socially mediated threat networks.
The idea also has implications for artificial intelligence. Even highly sophisticated AI systems can plan, communicate, and model social dynamics. But unless they can genuinely anticipate and care about future suffering — particularly in a self-referential or socially embedded way — they may fall short of this proposed threshold.
Under this framework, human consciousness is not defined primarily by intelligence, language, or tool use. It is defined by a vulnerable, temporally extended self that can imagine future harm, care about others’ fates, and be governed by threats that exist largely in shared symbolic worlds.
It is a darker lens on the mind than most theories emphasize. But it may illuminate something essential: the moment when imagination, empathy, and social power combined to create not just cooperation and culture — but coercion at scale. And that, paradoxically, may be one of the clearest signs that fully modern human consciousness had arrived.
Novel Thought Duration Is Unknowable A Priori
2026-01-28 08:57
I believe that it is impossible to know how long novel thoughts will take to think before one thinks them.
A corollary is that if you can predict the duration of thinking before it occurs, then such thinking is not novel.
This is a simple idea to me. Now. But a lot of novel thought went into it. It might not even be correct! What follows is an expanded explanation of the idea and why it might be correct and important.
Just like ones and zeros can do a lot in a computer, thoughts may have physiological mechanisms of the nervous system in common but they can be distinguished and classified by function. Many of our thoughts are autonomic — breathing, swallowing, Pavlovian body chemistry changes, etc. These thoughts are deeply internal and involuntary. They are safely set aside when considering discretionary thoughts.
Thoughts we have control over are quite interesting. They would seem to be the only non-reflexive mechanism the "me" of "myself" has to improve "my" life or even articulate what that could even mean. I propose that voluntary thoughts can be further divided into two major categories: primordial and serialized.
Primordial is Latin for "first + to begin". These are novel, original thoughts, ideas, and even feelings. I concede that it is possible that this kind of thinking may even be autonomic also. That is, you really may have no serious control over these kinds of thoughts. It is, however, a popular fiction that we do have some control over them so let’s go with that. I believe that one of the properties of primordial thoughts is that how long they will take to arise/complete can not be predicted.
Currently my opinion is that almost all other discretionary thoughts can be associated with serializing. Serialization is a a computer nerd word that describes the process of organizing an internal data state to be packaged and made ready to transfer to other computers such that the state can be recreated there. Similarly, serialized thoughts are mental states which are packaged up with some kind of formalized semantics suitable for communication. Note that the communication could be internal, for example with your own memory faculties or inner monologue.
If you can think of a thought that you have not yet had, but trivially could, I believe it can be explained as a component of some kind of serialization function. For example, if a tailor has never made a bespoke suit for me, but is confident that a pattern for one could be designed in a specific amount of time, the suit may be new and seem novel but the mental processes involved in producing it are well serialized. There is a lot more detail that could be explored to elaborate on the distinction between serialized and primordial thinking, but for now let’s definitionally assume the premise that primordial thoughts can not be timed and see how that fits our experience. Definitely send me (serialize) any challenging (primordial) counter-examples you think of.
Why are primordial thoughts not amenable to a priori duration predictions? The answer has to do with the concept of a solution space or feasible region. Mathematically this is a set of possible candidate solutions. Finding the optimal solution can be very hard. For example, the solution space for my safe’s combination is a million possible combinations with only one being correct. Without a (serialized) protocol involving statistics or safe cracking or me just telling you the combination, how accurate could your prediction be for how long it would take you to open it? It really is like asking, when will you win the lottery?
An interesting way to understand this classification of thoughts is to consider a form of "thinking" that is alien yet still familiar to us: the cognition of ants. Ants evolved to solve problems just like the neurons in our central nervous systems evolved to solve problems. Many ants busy themselves internally within their nest managing food storage, reproduction, and other essential ant matters. These unseen ants would be analogous to autonomic thoughts.
Ants can also be quite visible with external activities, primarily foraging. Foraging activities are like our discretionary thoughts. Most ants engaged in foraging have access to a pheromone trail to some known previously discovered food. The pheromone trail is an ant’s form of serialized thinking. When an ant sets out along a known trail, it is likely that the duration of the mission can be accurately predicted in advance based on how long it has taken other ants to make the nearly identical journey. It may also be possible to calculate the duration based on first principles of how long the trail is and how fast ants generally move.
However, if an ant will be going off piste to prospect for unknown food sources then the duration of its particular search is fundamentally unknowable. This kind of food discovery is the colony’s primordial thinking.
I’m trying to roughly cover the main concept here but of course there are fine points and subtleties. Even the tick of a precision atomic clock — one of the most time predictable things we know about — has timing uncertainty. And likewise, primordial thinking may have some strong hints or even hard limits. For example, an ant scout searching for a novel food source is definitely not going to take longer than an ant can survive alone without finding food. If it takes ten seconds to try a safe combination, opening my safe should not take more than 116 days of full time attempts.
What can we say if there is some smooth gradient between thoughts whose duration can be predicted and those that can not? It can still be helpful to talk about the two distinctly with some arbitrary dividing point, just as it is sensible to talk about yellow and green being different colors and not two radiation sources with nearly identical wavelengths.
Ok, so why might we care about this distinction? As ants require new sources of food, humans require new solutions to problems. I have observed that the (serialized) protocols for producing primordial solutions are frequently somewhat muddled.
Imagine ant management talking to the foraging supervisors who are reporting that the bonanza main food source enriching the colony is starting to run thin; a new source is needed. The supervisors propose sending out scouts in search of novel sources. At that point ant middle management interrupts and asks,
"How long will that take?"
Ant management has been managing the steady flow of food from the main source for so long that the business of acquiring food seems possible to accurately forecast. Unfortunately, the correct answer to the question is effectively forbidden around a manager who can fire you. The correct answer is, "I don’t know". Or better, "That is a degenerate question whose answer can not be known." (Followed by, "And here’s a link to a rambling essay making that case.")
Imagine a technical solutions company in the human world. The previous scene is roughly replicated but a new technical solution is needed to solve some new technical problem that has arisen. If a time estimate for the discovery of a solution can be given, then that solution has in some way already been discovered. If it truly requires primordial thinking, then it is not going to be possible to predict how long that might take.
Maybe what the company does is very similar to solving a Rubik’s Cube. One day a newly scrambled cube needs a solution. If the company has been just twisting identically scrambled cubes according to a static (serialized) recipe then a new cube likely will require some primordial thinking; how long that will take is unknown. However, if the company has already developed a generalized algorithm for solving cubes, then solving another starting position is really an exercise in serialized thinking which can also have very accurate predictions.
Note that the fact that accurate time projections for large solution space searches are routinely given does not make them valid. It makes them slightly incompetent fibs that muddle together with other projections because the distinction I’m outlining is not well appreciated.
If the distinction between primordial and serialized thinking can be so unclear it might be helpful to sample a non-exhaustive list of where primordial thinking tends to be required. What kind of thinking is primordial?
-
Pretty much any research by definition. Even following a very serialized procedure such as looking up words in a dictionary has a pretty big standard deviation for how long it takes me to understand what I need to understand.
-
The design of something that doesn’t already have extant examples to emulate. That something could be a tiny subcomponent of a common well established thing.
-
Taking a turn at chess or Scrabble or completing a crossword or jigsaw puzzle. Finding a terrain feature in Minecraft or a monster trophy in Witcher 3 is almost too analogous to ant scouting. I don’t know if it’s true for everyone, but some people enjoy primordial thinking.
-
Coming up with jokes or a satisfying melody. Free form jazz and extemporaneous rap music are very interesting activities.
-
Debugging a problem with an unknown cause. (We don’t like this thing happening; we don’t know why it happens; make it stop.)
-
Science done properly — both originating interesting hypotheses and the methods with which they might be falsified. The actual falsification effort itself however might involve following a well-established serialized experiment design. Also devising strategies for discovering things like new species or protein structures or celestial objects, etc. In this case, even the serialized follow through activity of the actual search may not be forecastable.
-
Managing a dynamic group of people is pretty open ended. What improvements should the group undertake? What is the optimal allocation of the group’s resources? Also related is crafting new laws, peace treaties, assistance programs, etc. Ironically, the people managing the activities of other thought-producing people are the ones most likely to ask for unknowable time projections for primordial thoughts.
-
Composition is interesting. Clearly coming up with ideas worth communicating requires some primordial thinking but even formulating extant ideas into a satisfying serialized form also requires primordial thinking. Once an ant finds a new source of food, a different primordial task begins as she tries to mark a serialized trail back to the nest.
-
Studying philosophy can be purely serialized, however practicing philosophy is primordial thinking almost by definition.
-
An example of primordial thinking I take a special interest in is software. Software is a computer’s structured thinking. Writing software then is necessarily thinking about thinking. And thinking about thinking is philosophy, which pretty much is always primordial. Crafting good software which solves a problem that software has not solved before must be primordial. I’ve already reviewed Brooks' Mythical Man Month, a book filled with software development challenges whose title hints at the profound difficulty of time forecasting the art. To create software that makes progress is hard and requires primordial thinking. How long this will take to do properly is unknown despite a team’s agility at making shit up.
Most of these examples cast an interesting shadow of counterexample. If my theory is correct, why is it then that AI can "think" about problems and provide satisfying solutions in a time span that can be predicted a priori? LLms can debug code, write poetry, play Dungeons & Dragons, etc. Even GOFAI image detection can find Waldo, if not quite instantly, in a very specific predicable amount of time.
I assert that the AIs are not doing primordial thinking. Mostly they’re just running an input vector through a network of calculations, perhaps scoring the result, and repeating that over and over. They repeat it not until the highest possible score has been achieved but until the allotted time runs out at which point the serialized process is terminated and the best solution thus far is presented. This is how a simple chess engine might work for example. Chess may be primordial thinking for you, but not for the computer. And that "creative" LLM output is exactly like a "creative" move of a chess engine. It is a clever illusion.
Could an AI be infused with primordial thinking with a random number generator or some other kind of non-deterministic process that makes the operational time unknowable? Possibly. I’m not sure. Maybe there are fundamental limits to this kind of thing and humans (and other animals) are already stumbling through the vast search space of ideas at a pretty good clip near this limit. Also, don’t forget that an LLM’s real primordial thinking happened during training. How long will AI training take to get the performance you desire? Hard to say!
As a starting point though, I think it is reasonable to say that problems that truly require primordial thinking have the following property: It is not going to be possible to say how long the solution will take. If you can, you necessarily have somehow already in essence solved it. To waste a creative human thinker’s energy with the forced fabrication of fantasy time estimates is distracting and robs mental resources that could be applied to the actual problem. If any such estimates are given, it should be well noted that the person giving them is comfortable "hallucinating" things.
I get it that middle managers really, really want to know in advance how long it will have taken once the clever idea is finally thought of. But wishing something were possible is not the same as it being possible. A more realistic strategy might be to try to figure out the point someone assigned to do some primordial thinking might become bored or frustrated by a lack of results. Allocate a shorter amount of time, but not frustratingly short either. Check back at that time and see how it’s going and evaluate if more time should be spent or if losses should be cut. It’s also important to note that if you are trying to extract creative ideas from people, giving them sufficient time to explore the idea is a kind of seed such that even when the project is restructured to not desperately require a particular solution, there is still a good chance that a creative thinker will think of something brilliant some time later in the shower.
Just remember if you are trying to extract a truly creative solution from a professional creative thinker and you are tempted to ask something like "How long will it take?" the answer must inevitably be, "Now, longer."
Xor Hides In Plain Sight
2026-01-17 07:16
In my last post I introduced the XOR logical operation and discussed some of the ways it is used. A few weeks after adding all of the bitwise logical functions to my programming language, I was nerd sniped by a fantastic opportunity to put them to the test. I received this fascinating spam email.
Most of you don’t see email in a text client. For those of us who still do, we are largely immune to many forms of graphical obfuscation and Javascript mischief. This means that I can safely take an academic interest in the technical tricks of spammers. Looking over this email I started to have a feeling that it was hiding something and when I got toward the bottom, I couldn’t believe my luck! Check out this line.
If you read my xor introduction you saw that the most common
programming language syntax for xor is the circumflex hat: ^.
And there it is center stage!
What are they using xor for? That big messy looking variable
definition of x gave me an instant suspicion.
let x =
"EAQQGVJeVkA......
It looks to me like they are trying to hide a malware payload in an encrypted string. And the interesting part, the encryption algorithm looks to be based on the xor function.
Yes, it turns out that one of xor’s other party tricks is that it serves as a simple low effort encryption algorithm. For encryption it is very fast but also quite weak, which makes it perfect for a job where it just needs to quickly scramble some text to slip it by an adversary known to have no decryption capabilities (normal humans and normal spam filters).
When presented with a (deliberately) messy piece of code today, you fortunately don’t have to burn too many brain cells working it out. I gave this code to my robot friend and it was confirmed — this code is definitely obfuscating some text using an extremely simple xor cipher.
How does an xor cipher work? We already saw that when you xor numbers you basically get a somewhat (very pseudo) random number. This means that you just need to xor your plain text with a password and you’ll not be able to read it. To get the plain text back from the cipher text you just need to xor the cipher text with the password again and the plain text will reemerge. Remember that toggle property? Using a passphrase to toggle between encrypted and decrypted is how this very simple xor cipher works.
Not only can our robot friends help us guess what’s going on, in cases like this you also don’t even have to trust your robot friends' guesses. I asked it to translate this code into readable Python which I could use to run and decode the payload myself. It did and I did, and we confirmed an xor cipher was exactly what this was.
At this point I got an ambitious stupid idea that cost me several days. I wondered, could I write a decoder for this encrypted text using only my own programming language? It would be a superb test of all the nice new boolean bitwise logic operators I have just implemented. And one of the last programming challenges where our robot friends can provide absolutely no help!
Fast forward many days of working on this and here we go. (Don’t worry about the details unless you like this sort of thing.)
'' ------------- Define variables.'' #
''EAQQGVJeVkIWDxEFVldAC1AIFRgCRQYCEgAnVVBfXFtCGlAVVlYdRFQxO084YFIPRExLAj8SGRUWVV0OThpGB1sCA1kZGQIHAk1FWFleVkIbQVAMXBlaFFwBCFhGHlhpRkVCGVJeVkIYQVAPXVZaHhsHBVJJEAIPCgoVFEFdSRhYU0cIXlVBD1oIRh9aPUNDRkUFVVpFF0ZXXFUDVkwbB1ECSREAWw8MEUgPVlFTVUYRGwprGRQVRlIKDkFPRAINAgcNQRtTXVEeFVANVVtCS0YFE18RQxBET15oGRUSGVJaXUZPSlVbAlcJGRgAUwdLQQQOVVpFFEVZQkQRShlBCRgDElUARwZOFQQMXVddQRIfCTtBGRQVAVkJFhgSVg0HBAoaF1RWXR0RU10NVkMYAFoUDEVGHlhpRkVCGVJeVkIYQUMCGQkVB0EJAx5GVitETUcwCRcZHlZ+FRoBdAJVTRIqGBFKFVoWRE5FY20VEhdkQRNKWVViBh5BOwNGHEQvC0JJG3teGx4Ra2ZGElRvFlVNQ1RTFUhBEg1AEhd+VBcdEF0LGx8XAmZEShRZX0FIRAcnGx4Vd0ERGVEzCFQeRGcQQx1GYg1ETQU4fFUZG29xEBpDY3MXTRc/URRKEDkVQU5FWwYVEhJaAhZKHntjQR4GI3UBHEE1I0dJG1lnGx5WfkYBElQIW1VPSkxaPUNDRkUFVVpFF0ZCS10EF1dGFWEDGUJBCkNEFgoRUEFbVlsMElcIQVFRXRUPD0UEQ1lDVhUaAhVFUFFCWgtBCAQFQw5GCVMIUAsXXEVTCQUXAhVUXUMFXEYPRgUWGQ1BWgIRAQwMAxUCSU0NQlAFXV1bAQ9GUUYZDEMMEAAQX1ldTg8WWlgFXVFbXRUcTF8PUwYbXEVbAAwLAAwNFQprGRQVRlEJAkMMUg0XSBIQUEFXEVJaXUZIAj4VRhVGBVkCQg4GCBFMW1pWQBtfXF8ES3xhK3lGXBZDFVhpRkVCGVFdWkBbV18VF1ZaAkxIAEYRUg0HJQ0LVVEaXllZRRha'' |U sto
''feb95295621a945f5fa6a7cc'' ''key'' #
dup len ''length of key'' #
U len swap / ''size of X / size of key'' #
ceil * ''pad out beyond (ceil) what is necessary'' #
|i sto ''i is sufficiently repeated key'' #
'' ------------- Helpful abbreviation.'' #
|[2 base::!] |b sto
'' ------------- Make a lookup table.'' #
''ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'' |b64map sto
'' ------------- Go through each quad and pull out a padded 24bit binary number.'' #
U len 4 / range 4 * 1 + ''Generate a quad index list: 1 5 9 13 ... 1037 '' #
|[ 4 range + |[U swap get b64map swap pos -- b dup len 6 swap - ''0'' * swap +::!] for + + + ::!] for
'' ------------- Put the 24bit binaries in a list'' #
U len 4 / 2list
'' ------------- From 24 bits pull 3 chars'' #
[] |X sto
|[ dup dup b 16 bsr rot b 8 bsr 255 band rot b 255 band 3 2list X swap :+ |X sto ::!] for
'' ------------- Iterate through ciphertext, apply XOR, save plaintext P'' #
'''' |P sto
X len range 1 +
|[dup X swap get swap i swap get num bxor chr P swap + |P sto::!] for
P
The hard part was actually doing an initial decoding from
base64, a common encoding format
used to protect binary data that is transported in readable text
characters. With Python, you can just import base64 and it’s easy,
and in Javascript it’s even easier — note the
atob function in
the spam code (which is alphanumeric to binary).
Of course I had to implement my own which was another great test of my
new boolean logic functions.
But once the message is converted back into (unreadable) binary it
really was as simple as doing an XOR operation on each cipher text
byte with each character of the passphrase; when you run out of
passphrase characters, just go back and loop to the passphrase
beginning again (in the original JS done with: c % q.length). This
is very low security. The passphrase is included right there, it is
the string that starts with feb95....
Ok, so what is this spam mail trying to get a promiscuous mail client’s Javascript "feature" to run? Here’s the output of my Geogad version.
I’m keeping the dangerous code confined to images for you — to create
mischief you’ll need to type it in. But we can see that it seems to be
doing a lot of allow functions, presumably to lower your normal
defenses. I’m no JavaScript web security expert so I asked my robot
friend to tell me what this is doing. (Again, ignore the details if
you’re not interested - I wasn’t!)
-
allow-same-origin- Allows the <iframe> to access the same origin as the parent page (bypassing same-origin policy). -
allow-top-navigation- Allows the <iframe> to navigate the top-level browsing context (e.g., redirect the user to a malicious site). -
allow-modals- Allows the <iframe> to open modal dialogs (e.g., popups). -
allow-scripts- Allows the <iframe> to run JavaScript. -
allow-popups-to-escape-sandbox- Allows popups to escape the sandbox (highly dangerous). -
allow-forms- Allows the <iframe> to submit forms.
Yikes. None of that sounds pleasant.
One final mystery is the glow.src definition. First of all, I’m
baffled by their exuberant use of mixed quote styles (but of course
I am a snob when it comes to text
quoting). It seems to be another text string encoded with base64.
Since I had to work it all out anyway, here is a general base64
decoder written in Geogad that decodes this text.
[ ''aH'' ''R0'' ''cH'' ''M6'' ''Ly'' ''9u'' ''ZX'' ''Rs'' ''aW'' ''Z5'' ''Lm'' ''Nl'' ''YW'' ''Zp''
''b2'' ''th'' ''Lm'' ''lj'' ''dS'' ''8h'' ''bE'' ''Nt'' ''R1'' ''Rv'' ''Un'' ''ZE'' ''ZG'' ''ZG''
''Y0'' ''Zv'' ''b3'' ''l0'' ''OV'' ''BC'' ''VE'' ''lU'' ''Lw'' ''AA'' ]
'''' swap |[+::!] for |U sto
|[2 base::!] |b sto
''ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'' |b64map sto
U len 4 / range 4 * 1 +
|[ 4 range + |[U swap get b64map swap pos -- b dup len 6 swap - ''0'' * swap +::!] for + + + ::!] for
U len 4 / 2list
[] |X sto
|[ dup dup b 16 bsr rot b 8 bsr 255 band rot b 255 band 3 2list X swap :+ |X sto ::!] for
X |[chr::!] for X len 2list '''' swap |[+::!] for
When we run that we can see the URL this malware reports to.
Obviously opening this URL is a bad idea. I think this
.icu TLD is a beautiful example of
how
fucked DNS is these days. I was really curious what "icu" was an
abbreviation for, but I have come to believe it is meant to be read as
"I see you". Totally not creepy at all!
Amusingly, when first presented with this problem, my robot friend went on to confidently say it had preemptively worked out the binary to ascii translation itself and proceeded to implicate a completely hallucinated Cuban URL.
I guess that when you have a very sophisticated LLM that can walk through the code with you and still fail to properly get the right answer, it demonstrates why this spam tactic might still be effective. And that demonstrates why it can be useful to have your own tools for doing bitwise math operations so you can decode it yourself!
Xor - More Than You Wanted To Know
2026-01-16 20:57
Last month I realized that my programming language lacked a convenient way to do bitwise logical operations and so I added functions for them! This may seem like some freaky esoteric stuff but for serious programming (especially C programming) it comes up quite a lot. Especially when working on C I frequently need to test little logical constructions and having my extemporaneous calculator language be capable of trying out things like this on the fly can be very helpful.
What exactly is xor (or often XOR or even EXOR)? Xor is an especially interesting function. It is the logical exclusive OR function. In 1969, AI pioneer Marvin Minsky co-authored a book which claimed that a single layer perceptron in a neural network could not compute xor. This is technically correct. This was interesting because other logical functions were possible. Wikipedia has details. Some people extrapolated this to mean that neural networks were fundamentally limited — a pointless dead end. Those people were famously incorrect.
First, let’s have a quick review of the three logical functions normal people are already familiar with. A logical function takes logical states (i.e. either true or false) as inputs and returns logical states as outputs. Imagine I am the CEO of a company and I will decide to do a venture or not (true or false on doing the venture). I have two advisers, Mr. A and Ms. B. If I require that both A says it is a good idea AND B says it is a good idea then my decision is A AND B. Alternatively, I could go with either A OR B — when one adviser saying it is a good idea is sufficient, then that would be A OR B. Easy. The other common logical operation is NOT. You can think of it as a filter for incompetent advisers; simply if A says do it, I don’t, and if A says don’t do it, I do. All of that is easy and we all know how to use those naturally.
So what’s the deal with XOR? The first thing to note is that covering the other logical operators was on task. Don’t get too bogged down with the details (unless you like that sort of thing) but you can actually build xor with them.
A XOR B = (A AND NOT B) OR (NOT A AND B)
Or to see how it looks in (an HP48 style reverse) lisp here is the entire (Python) source code that composes the new binary xor function in my language showing that with the other operaters in place, xor was basically a bonus.
internal_syms['bxor']= build_GG_command('dup2 bor rot rot band bnot band')
Of course that is gibberish to most people so let’s return to a more human perspective to understand what this xor is really all about.
XOR works like this: if Mr. A and Ms. B both think an idea is bad, then we don’t do it. Sensible enough. If A thinks it is good but B doesn’t, we’ll do it and also if B thinks its good when A doesn’t we’ll do it. That is just like regular OR. But the weird thing about xor is that if both Mr. A and Ms. B think it is a great idea, it will be rejected. Maybe the boss suspects untoward collusion or something like that. You can also suppose that both A and B agreeing is not "or-like" enough, hence the name "exclusive or".
Fortunately it is easy to spell out what all the important logic gates do (I just did) and the only one that’s weird, xor, is easy to memorize. But to really understand it you have to have some sense of why you’d ever care about this weird quirky logic thing. It pops up more frequently than you might expect.
Let’s start with a physical model. Imagine I’m designing some kind of storm water management system and I have two pipes from two flood prone regions, call them A and B. If neither region floods, ok, obviously nothing happens in the drain system (output is "false").
If A floods or if B floods, we want the drain system (at the bottom of the device) to politely handle the water.
The interesting part is if both regions flood simultaneously, this amount will overwhelm the drain system and we just want to dump all the water in the ocean (ahem San Diego).
That drain at the bottom of the diagram is essentially a fluidic XOR gate. And the drain to the ocean in the middle is actually an AND gate.
You can make entire computers with fluids out of devices like this but it is not really the most useful application of xor. It does demonstrate that the wierdness of xor’s behavior can map to real world applications.
Perhaps the most common situation where xor logic is in our daily lives is a 3-way light switch where a single light is controlled by two switches. If it is true that switch A is up (1) and switch B is also up (1), then the light is off (1 XOR 1 = 0). If both A and B are down the light is also off (0 XOR 0 = 0). But if the switches are different, then the light is on (1 XOR 0 = 1; 0 XOR 1 = 1). My house wiring was installed by drunk children and I have two 3-way controlled lights — one of set is XOR as described and one is NOT XOR!
You can build a logic gate that does this using transistors and therefore you can etch them right on to semiconductors. That is important but let’s explore xor’s software applications which is where I personally encounter it.
Toggle
If you xor a binary number with all 1s, you get the bitwise complement.
0010 XOR 1111 = 1101
And…
1101 XOR 1111 = 0010
This is the same as the NOT operator but interesting to note that NOT is achievable with xor.
Swapping Integers Without Temps
A very common programming task is taking the contents of one variable
and swapping it with the contents of another. Sure, Python has always
had some superb syntax for this (a,b=b,a) but that underlines how
important this is to programmers. In languages like Java and C++ you
usually see this kind of thing.
int temp = A;
A = B;
B = temp;
Pretty comprehensible and very common. But there is another way! A way
that interestingly does not require a temporary variable. In many
languages the ^ is the xor operator.
A = A ^ B; // A = A XOR B, alternatively A^=B
B = A ^ B; // B = A XOR B, or B XOR A, or B^=A
A = A ^ B; // A = A XOR B, alternatively A^=B
Here is the short form. Kind of magical really.
A^=B; B^=A; A^=B; // And the values are swapped!
This only works for integers but it is still pretty cool.
Note that this is mostly just a party trick. A modern Pythonic tuple
reference swap is actually 4.5x faster than the XOR method, probably
because it is a single cycle atomic operation. C++'s std::swap(A,
B); is impressively twice as fast as the XOR method. So save this
trick for when the memory concerns of a temporary variable are
relevant. Or code obfuscation.
Zeroing
Very often one needs to reset a variable such as a counter back to 0 to restart a loop. How can you ensure that your counter is zero? The normal way is simple.
c = 0;
But there’s another stranger way.
c ^= c;
By feeding an xor function the same value for both inputs, you can be guaranteed the result will be 0. While that seems pointless to normal programmers, sometimes this can be useful. Remember that XOR is one of the most efficient operations a computer can do and is worth exploring when optimizing code. Normally your compiler knows all these tricks and behind the scenes it is very plausible that it could be generating machine instructions for zeroing out your counter like this.
XOR EAX, EAX ; EAX = 0
LOOP_START:
; Do something
INC EAX ; EAX = EAX + 1
CMP EAX, 10 ; Compare EAX with 10
JL LOOP_START ; Jump if EAX < 10
This is an assembly language loop that does something 10 times. Note the first line.
A related thing xor can do is if you have some bits that you want to monitor for changes. You can take yesterday’s bits and xor it with today’s bits and if it doesn’t turn out to be zero as described, something has changed.
Checksums
Imagine you have a list of arbitrary binary numbers.
01111111001100100110
00011100011011101011
11010100010010111011
What would happen if you used AND twice?
00010100000000100010
You can see that there are 4 positions where all the bits are 1. You can see that if we keep doing this with more numbers, the trend is that the final answer will be all zeros after a while. What about OR?
11111111011111111111
A very similar deal except trending to all ones. But what about xor?
10110111000101110110
This is distinctive. It is also repeatable and these very basic logic operations are some of the cheapest to compute. This means that if you wanted to transmit the original three binary numbers and be pretty sure everything transferred correctly, you could transmit one extra number. That extra number is the xor checksum. The receiver then computes it from the three transmitted numbers, and it needs to match the one the sender computed too.
(Of course there are fancier checksums. The checksum standard RFC1071 says "…XOR is unsatisfactory for other reasons". But never says what those reasons are. While not especially robust, XOR operations are more robust than parity bits, which are used in error correcting memory.)
PRNG
If you can manage to generate a very arbitrary checksum, there’s almost a random property to it. The xor operation can be the "pseudo" in Pseudo Random Number Generators (PRNG). You could seed it with something really random or keep a replayable seed handy, but if you need mildly random numbers for very little computational cost, xor is a simple possibility.
BitBlit
An interesting use of xor is in (ancient) graphics programming to animate sprites. Let’s focus on a simple black and white example that everyone is familiar with: the mouse pointer icon. When you move your mouse around, how does that work? The icon is mapped out with true (white) and false (black) bits in, yes, a bitmap. The screen that the pointer moves on is also a big map of bits. The mouse position is read from the HID (human input device) and a position for where to put the icon is determined. Then the bits that correspond to the size of the pointer icon’s bitmap are read from the screen’s bitmap. The pointer icon bits are then xor merged with the screen bits. This leaves the bits around the pointer arrow itself untouched. The magic of this and why it is useful in dynamic animated sprites like a mouse pointer is that when the move needs to happen you basically need to put the original bits back exactly as they were. With the (simple 2-bit black and white) setup I’ve described, you can reset to the original screen bitmap’s bits by doing XOR on the pointer icon bits a second time. Like the toggle example, this toggles the presence of the pointer icon on, and then off.
This kind of xor based sprite rendering is pretty old fashioned. If you’ve been paying attention you’ll recall that if the pointer icon’s bit is white and the screen’s bit is white, that will then be black. This means the sprite is always visible in every bit, but sometimes color inverted.
Canvas: Sprite: Bitblit: Sprite: Canvas Restored:
******** ******** ********
******** * **** *** * ********
******** *** *** ** *** ********
******** XOR ***** = ** * XOR ***** = ********
******* ******* *******
*** *** ***
*** *** ***
*** *** ***
This works pretty well for selection boxes and some other items but practically deploying xor for graphics is a dying art. Still, the graphical application can stimulate ideas for other cases where something needs to temporarily rampage around memory.
Those are some of the interesting applications of xor. I’ve got one more interesting application to cover, but it is interesting enough to deserve its own post. Which I have now posted: https://xed.ch/blog/2026/0117.html
Emphatic Thermometry
2026-01-08 19:55
The start of winter has been intense for us. We have had nearly five weeks where the temperature almost never got above freezing. One of those times that it did preceded some of the toughest winter weather I’ve ever seen.
Finally yesterday we returned to milder, normal winter weather. It turns out that being outside in -6F/-17C is a very different experience than the 41F/5C we got today. Exactly what the temperature is turns out to be a very important detail!
How can we know what the temperature is? Assuming it has not yet been bombed by fascists, we can ask the good people at weather.gov. But there are a lot of micro-climates in my area and, for example, they’re off by 2F/1C right now; in other words, perhaps their reading is correct, but for some other place near by.
We could buy one of these.
I bought this almost exactly three years ago and when I moved here the remote sensor stopped working. So that was very frustrating. It does a few things but an outdoor temperature measurement was really the job I cared the most about.
In our bedroom we have a clock with an external temperature sensor but I’m finding that on the coldest most interesting nights, the AAA batteries suffer and just can not transmit the readings. Also replacing disposable batteries at all is suboptimal for this kind of permanent application.
I recently ordered one of these Toko snow thermometers.
I didn’t really know what it was but it turned out to be a bulb thermometer in a protective cover. Ok, fine. That’s simple. I liked my last bulb thermometer that I used in the 1990s — until it suffered from a "separated column". I used this Toko thermometer twice before it becaome useless after suffering the same problem.
Ok, this was all starting to make me grumble. As a computer nerd I knew that this just can not be so damn hard. I was reminded of a project I had done in 2008 where — with the help of a Russian robotics genius — I got a DS1820 analog temperature sensor publishing to a 1-Wire bus and then FUSE mounted into a Linux filesystem. That worked fine and I still have the hardware. Here is a photo of it in action with a computer that still had an RS-232 serial port!
I figured, ok, I don’t have access to my Russian friend, but surely after 18 years, this kind of nerdery is a little less esoteric and surely it has improved quite a bit. Indeed it has!
Looking around I found this nice video from DroneBot Workshop and he did a good job covering the features of various temperature sensors.
I was so impressed that when the video went on to describe a plausible system that could be built, I was ready to not second guess it.
He basically recommended using a Texas Instruments TMP117 temperature sensor. I noticed some of the sensors had a low range of 0C which would be pretty much useless to me. Many have a lower limit of -40C/F. The TMP117 with its -55C/-67F seemed just right. The sensor itself is $3.40 — or $2.27 if you’d like 1000 of them.
The next question is what kind of computer would interact with this sensor? I could have used a Linux friendly Raspberry Pi Zero or something like that, but it turns out that if all you need to do is run a single program that just reads a temperature sensor, having a sophisticated multi-user, multitasking operating system is quite overkill.
What is needed is a microcontroller. He had selected an Espressif ESP32-C6. If you buy just the IC (integrated circuit, i.e. the chip itself) you can get them for $1.86. This is a 2.4GHz 32-bit RISC processor with 512kB of RAM. This tiny chip also contains a radio and antenna for WiFi and Bluetooth. And a gazillion other astonishing features — here is its datasheet.
In theory I could buy the raw surface mount ICs and do microsurgery with a soldering iron to hook them up. But I’m more realistic than that. It turns out that ICs like these usually are sold to big companies who are integrating them into their own circuit boards. Or they are sold by a kind of middle man who makes what is called a development board. The dev board basically has the IC mounted properly to it (generally with some Taiwanese surface mount solder voodoo). The board has leads to each of the chip’s pins so that it’s easier to connect things. Some boards add bonus features like LEDs and push buttons that serve very common helpful functions.
The project I was following along with went with the Adafruit TMP117 dev board. $11.50.
Notice how the actual TMP117 IC is on a little cutout island. This is to avoid picking up heat from the circuit board itself.
Ok, that’s nice. But for the other components he used Sparkfun dev boards. Since shipping is a big portion of the cost, it seemed dumb to split the parts into two sources. I took a huge gamble that the Sparkfun TMP117 dev board would also be suitable. $16.95.
Spoiler alert - thankfully it worked fine. Also note that this board didn’t put a (heat generating) resistor on the thermally isolated island.
For the microcontroller devboard I stayed with the SparkFun Qwiic Pocket Development Board - ESP32-C6. $19.95
And for $10.95 it seemed like a good deal to add a completely optional OLED display.
Once all this stuff arrived I was still not really at the starting line. The sensor and the display can’t do anything without the microcontroller, and the microcontroller can’t do anything without a program to run. This thing needed to be programmed.
Step one was simply getting a program to print out a "Hello world" confirmation message. Easy, right? Well, no. I spent nearly two weeks fucking around with this. To install software on a microcontroller one generally needs to use a program called Arduino-IDE, the annoying GUI code editor for developing Arduino brand chips. Why did a whole new extraneous company just get interjected? Because as smart as electrical engineers usually are, this is where they are stupid. When it comes to software, they try as hard as they can to not understand what they are doing. At least that’s what Arduino-IDE is screaming to me.
I found this GUI bewildering and hostile to my refined Vim sensibilities. I had to claw my way to getting any functionality out of this device including a harrowing descent into the madness that is Windows (on a qemu VM).
Here is a brief look at this shite programming quagmire where I am trying to program literally the simplest thing possible.
You can see I needed two projects (sketchily called "sketches") to be open so that I could see both the compile output (most of which was disabled by default) and the serial output at the same time. Especially on Windows, the compile took forever and these people seem to be unaware of make or a sane DAG build tool.
If you want to read about all of my frustrations with the process, here is my detailed forum post.
Eventually I got it to do something from Windows. Then I got it to finally output to the serial bus. Then I got it to work from Linux. Then I finally got it to run software which I had modified from the project template I was following. At last I was in control of this thing and on the software starting line.
Here’s my setup assembled and finally working.
Here it is outside hooked up to a couple of USB rechargeable battery packs.
Here is a demonstration of the ESP32 running a web server that I can contact over wifi; it presents the temperature in a custom web interface.
I am more comfortable looking at a command line output where I can see
the trends a bit more as shown in the terminal window. For example,
that perspective immediately tips one off to the fact that in the
original project software, redundant readings (fahrenheit) are
wasting bandwidth; I would have chosen to send the original sensor’s
exact bits with no degradation. But obviously now that I control
this thing, the only limitations are the fundamental laws of physics.
There is still a lot to be done with this project. I’d like to add more sensors and i need to do testing on battery charging and radio range. But I now finally have a system that produces highly accurate temperature readings that I have complete control over and which are transmitted to me conveniently on demand over wifi. Finally some progress.
For older posts and RSS feed see the blog archives.
Chris X Edwards © 1999-2026