The plant on its own terms โ
Everything above compares the plant against a reference: a species profile, a wall clock, a table of healthy ranges. That is where most plant monitoring quietly goes wrong, because the reference is a population average and your plant is one individual, in one pot, in one room.
These four layers drop the reference and compare the plant against itself.
๐งฌ Electrome fingerprint โ what is normal for this plant โ
A signature of the plant's baseline electrical state across several axes: complexity, entropy, variability, and the distribution of power across physiological bands. The library learns it over the first windows, then reports departures from it.
const s = await plant.listen( { seconds : 600 } )
s.shift.verdict
// "Learning this plant's normal: 6/12 windows."
// then, later:
// "Signature has moved: complexity down 38%, band power shifted low.
// This plant is not behaving like itself."
s.shift.delta[ 0 ] // { axis: 'complexity', direction: 'down', weighted: 0.41 }plant:electrome-shift fires when the signature genuinely moves.
Two design decisions carry most of the weight. The fingerprint is DC-offset invariant โ an electrode drifting by 40 mV is a wiring fact, not a new plant, and must not read as one. And the baseline refuses to judge before it has settled: a "normal" derived from a single window is not a normal, so early calls say so rather than inventing a verdict.
๐ Internal clock โ what time it is for the plant โ
circadianHealth asks whether a rhythm exists. This asks the question that actually changes behaviour: where is the plant in its own day, and how far is that from the clock on the wall?
s.clock
// { periodHours: 26.4, acrophaseHour: 15.2, subjectiveHour: 4.1,
// offsetHours: 2.2, freeRunning: true, aligned: false,
// verdict: 'Free-running at 26.4h rather than 24h. The plant is following its
// internal clock because the light cycle is not strong or regular
// enough to entrain it.' }
timingAdvice( s.clock, 'probe' )
// { good: false, betterInHours: 5.9,
// reason: 'A stomatal probe at subjective night measures a plant that has
// closed down. The response would read as "weak" for reasons that
// have nothing to do with health.' }A plant whose subjective dawn falls at 3pm is not "arrhythmic" โ it is entrained to something you did not intend: a corridor light, a west-facing window, a lamp on a timer. Every decision made on wall time is landing at the wrong point in its day.
๐ก Two-site coherence โ is this the plant, or the electrode? โ
The electrode is the weakest link in the whole evidence chain. A dry contact or a callus forming produces a confident, well-shaped waveform that means nothing, and every layer downstream then reasons beautifully about an artefact.
Two electrodes fix what one never can, because plant signals propagate at speeds physiology constrains:
| Event | Speed | A 50 mm gap implies |
|---|---|---|
| Action potential | 1โ40 mm/s | 1.2 โ 50 s |
| Variation potential | 0.5โ5 mm/s | 10 โ 100 s |
| System potential | 0.1โ2 mm/s | 25 โ 500 s |
await plant.attachSensor( {
driver : 'electrode', transport : 'synthetic',
sites : [ { id : 'stem', distanceMm : 50 } ],
} )
s.coherence.stem.verdict
// "Both electrodes saw the same event, 5.00s apart across 50mm. Implied speed
// 10.00mm/s is consistent with an action potential (1-40mm/s).
// This is the plant, corroborated at two sites."The failure modes are the point:
- Zero delay is diagnostic. Nothing biological reaches two separated points at the same instant โ but mains pickup and ground loops do exactly that. A simultaneous event is rejected as interference, not accepted as a strong signal.
- Overlapping speed ranges are reported honestly. 2 mm/s fits all three event classes, so the library returns every candidate and names none. Claiming "variation potential" there would be false precision.
- An ambiguous lag corroborates nothing. A periodic signal correlates just as well at lag as at lag + period, so when rival peaks come close the result is flagged ambiguous and coherence is withheld โ match discrete events instead.
That is corroboration grounded in physics rather than statistics, and it is very hard to fake.
๐ต VPD-aware blue โ the same response, opposite meanings โ
Blue light opens stomata. So a strong blue response was read as a healthy, responsive plant. That reading is wrong roughly half the time, because it ignores the air.
Vapour pressure deficit โ how hard the atmosphere is pulling water out of the leaf โ is now computed from temperature and humidity and folded into every blue reading:
| Blue response | VPD | Soil | Reading |
|---|---|---|---|
| strong | high | dry | ๐ด demand_with_deficit โ transpiring hard against a supply it does not have. This precedes sudden wilting. |
| strong | high | wet | high_demand โ demand, not comfort |
| weak | low | โ | no_demand โ still humid air, not water stress |
| weak | high | โ | closed_under_demand โ refusing to open under strong pull is a clear ABA signal: stress |
plant.context().vpd // 2.14
plant.context().vpdBand // 'severe'The same closed stoma means "nothing to do" in still humid air and "the plant is defending itself" in dry air. Reading it without the atmosphere is how a monitoring system talks itself into watering a plant that is fine, or reassuring you about one that is not.
