Where your Mac's temperature numbers come from, and why two apps disagree about them
Two temperature apps on the same Mac rarely agree, because they read different sensor paths and summarize unlabeled diodes differently. Here is CoolCurve's own pipeline, including what it refuses to guess.
On this page
Open two temperature apps on the same Mac and they will often show different numbers for what both call CPU temperature. Neither app is lying. They are reading different sensor paths, sampling at different moments, and making different decisions about what a dozen unlabeled heat sensors add up to. CoolCurve, on Apple silicon Macs running macOS 13.0 or later, takes the maximum across that cluster instead of an average, and it will not tell you which physical core produced the result, because it does not know.
I build CoolCurve. Nothing here is a benchmark. Every number below is a constant CoolCurve's own code checks for, or a limit a private Apple API puts on it, and this post names which for each one. No fan speed and no temperature was read off a machine for this article.
Two readings, one Mac, and neither one is wrong
You have probably done this: opened CoolCurve next to some other sensor tool, pointed both at the same idle Mac, and watched the CPU figure disagree by several degrees. The instinct is to trust whichever number is lower, or looks more official. Neither instinct helps. The two tools are not disagreeing about the temperature of anything in particular. They are reporting separate decisions: which sensor path to read, how many samples to throw away as noise, and what to do with a cluster of individually unnamed heat sensors nobody outside Apple has published a map for.
This post is about the decisions CoolCurve makes, not a defense of one number over another, and at least two of them are limits I would rather name outright than let a reader discover on their own.
The sensor path that was never meant to be read by you
macOS exposes hardware temperatures through a private API, IOHIDEventSystemClient, that Apple ships but does not document for outside developers. CoolCurve does not link against it directly. At runtime it opens /System/Library/Frameworks/IOKit.framework/IOKit with dlopen and resolves six functions by name with dlsym, so if Apple ever renames one of those symbols in a future release, an app linked against it directly would fail to launch outright. Submix leans on the identical resolve-by-symbol-name trick for its own permission check, for the same reason: a private framework can rename a symbol on either side of a macOS update. CoolCurve instead falls through to a second, older path and keeps running.
The service CoolCurve asks for is matched by two numbers, a primary usage page of 0xff00 and a primary usage of 0x05, both in Apple's vendor defined range rather than any documented HID usage table. Each reading that comes back is a temperature event, and CoolCurve reads the float value directly rather than trusting whatever label came attached, because the label is the least reliable part of what the API hands back.
Treating three identical samples as a lie
Not every channel this API reports is a working sensor. Apple's firmware mixes calibration constants and a handful of permanently dead channels in among the live ones, and nothing in the API names which is which. CoolCurve's answer is to take three samples per channel, 40 milliseconds apart, and check whether all three land bit for bit identical. A real analog sensor jitters by hundredths of a degree even sitting still. Constants do not. Three identical readings in a row are the signature of a constant, not a temperature. Battery, NAND and SSD channels are the one exception, exempted by name, because they legitimately hold steady across a burst that short.
This is a filter built from an observed hardware quirk, not a defensive guess. It has a real cost. A genuinely still moment on a real sensor could in principle look like a calibration constant for one poll. CoolCurve accepts that trade. The alternative is a dashboard full of constants sitting next to the handful of channels that actually matter.
A reading near minus 9201 degrees is not a malfunction
Filtering by repetition is not the only guard. On an M4 Pro, several of the channels this API exposes are not wired to anything CoolCurve wants: they return a fixed sentinel near minus 9201 degrees Celsius, and one channel labelled tcal never moves at all. CoolCurve discards anything outside a minus 40 to 150 degree band, and drops any channel whose label contains tcal, regardless of what it reads. None of that is published anywhere Apple maintains. It is a fact about what the firmware actually returns, and the filter exists to catch it because a real chip handed the code exactly that number.
More than a dozen sensors, collapsed into one row
Apple's chips do not expose one temperature per core, or even one per named thermal zone. An M4 Pro reports more than a dozen separate diodes under generic labels, alongside one SSD reading and one battery reading, and nothing in that data says which diode sits under which performance core or how close any of them are to the GPU cluster.
CoolCurve collapses that entire unlabeled group into a single row, labelled CPU, and the value shown is the maximum across the group, not the average. That is the harder choice. An average produces a calmer, smoother number, and it also buries the one diode actually running hot, which is the number a fan curve has to react to. A fan cooling toward the average while the hottest core keeps climbing is not doing the job it exists for.
The row's underlying key is the literal string SoC, chosen so a curve bound to it keeps resolving to the same source even as the diode behind the maximum shifts from poll to poll. Point a curve at that row today and it still reads the right thing next week, even though the hottest sensor almost certainly will not be the same one.
What CoolCurve will not tell you, on purpose
This is the part where I would like to tell you which diode belongs to which performance core, and I cannot, because nobody publishes that map for any of these chips. Guessing would be easy: pick a plausible label, attach it to a number, and most readers would never check. CoolCurve does not do that. It shows one aggregate number, calls it CPU, and does not pretend to know more about a chip's floor plan than Apple has ever documented.
That refusal comes with two limitations worth naming outright. First, CoolCurve cannot tell you which physical diode, or which core, produced the number in that CPU row, and it will not manufacture a per-core view it cannot stand behind. If a future macOS release documents that mapping for a given chip, the row can get more specific then. Until it does, the row stays honest about being an aggregate rather than pretending to precision it does not have.
Second, reading any of this, the sensor list, the CPU row, the whole dashboard, is a paid product feature, not something left running once a trial ends. Exactly one behavior works regardless of license state, trial expiry, or whether the app can reach its own configuration service at all: handing your fans back to Apple's automatic control.
When the private path answers with nothing
Not every Mac, and not every macOS release, answers the private HID request the same way, and when it returns nothing CoolCurve falls back to reading the System Management Controller directly, the same low level interface fan control itself depends on. That fallback walks the SMC's entire key table, keeps only the keys it can name with confidence (a short list of exact matches, plus prefix families for battery, GPU and storage), reads each surviving key twice 40 milliseconds apart, and discards anything reading exactly zero both times or falling outside that same minus 40 to 150 degree band.
That walk runs to a few thousand individual IOKit calls. CoolCurve performs it once and caches the result rather than repeating it on every poll. On a Mac where the private path never resolves, the first snapshot after launch is the slow one. Every snapshot after that reuses the cached list, polling every two seconds while the dashboard is open or a fan is under control, and every five seconds otherwise.
So which number should you trust
Two apps disagreeing by a few degrees at idle is not a malfunction on either side, and the smaller number is not automatically the more trustworthy one. What matters is which decisions produced it, not its size.
CoolCurve's own answer, take the maximum, filter on repetition and range, refuse to invent a per-core map, is written down above because it is checkable, and the full feature list shows what else the dashboard does. Open the sensor list, put a core under load, and watch the row track the hottest diode instead of settling toward an average.
If a loud fan brought you here rather than a mismatch between two apps, the nine step diagnostic walks that search in order and links back to this pipeline at the step where sensors matter. If the question is which fan app to buy at all, that is a different post, a capability comparison across eight apps, and this one was never trying to be it.
Questions people ask
Why do two temperature apps show different numbers for the same Mac?
They are not reading identical inputs. Different tools read different sensor paths, apply different sample filters, and summarize a cluster of unnamed diodes into one figure differently, and a few degrees of disagreement at idle is not evidence that either one is wrong.
Does a higher reported number mean an app is more accurate, or can CoolCurve just tell me which one is right?
Not by itself, and no. A tool reporting the maximum across a sensor cluster will usually read higher than one averaging the same cluster, and the maximum is the more useful number for anything reacting to heat, since an average can hide a single hot diode. CoolCurve can explain what its own number represents. It has no way to inspect a different tool's pipeline, and will not speculate about one.
Why does CoolCurve show one CPU number instead of one per core?
Because Apple's own sensor data does not label individual diodes by core or zone. CoolCurve would have to guess which physical sensor sits where, and treats that guess as worse than an honest aggregate.
Is temperature monitoring free in CoolCurve?
Not always: it runs during the trial, but reading sensors is a licensed product feature in the shipped app, not a permanently free tier. The one behavior that keeps working without a license is returning your fans to Apple's automatic control.
What happens to fan control if the sensor data disappears mid session?
If both the private path and the SMC fallback come back empty for two consecutive polls, roughly three seconds, CoolCurve treats that as a fault and hands every fan back to firmware rather than holding a target with no thermal signal behind it.
If you want to watch this pipeline running against your own Mac rather than take the description on faith, the trial puts the sensor list live from the first launch.
Requires macOS 13.0 or later, Apple silicon only. CoolCurve is a direct download, and more notes like this live on the CoolCurve blog.