<?xml version="1.0" encoding="UTF-8"?>        <rss version="2.0"
             xmlns:atom="http://www.w3.org/2005/Atom"
             xmlns:dc="http://purl.org/dc/elements/1.1/"
             xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
             xmlns:admin="http://webns.net/mvcb/"
             xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
             xmlns:content="http://purl.org/rss/1.0/modules/content/">
        <channel>
            <title>
									Welcome, please register to post topics or comment! - Recent Topics				            </title>
            <link>https://cyclesresearchinstitute.org/community/</link>
            <description>Harmonics and Cycles Forum for scientific discussion and the pursuit and sharing of knowledge on all things harmonics and cycles. Please register and confirm your email if you wish to comment or post topics.</description>
            <language>en-US</language>
            <lastBuildDate>Wed, 02 Sep 2026 07:02:54 +0000</lastBuildDate>
            <generator>wpForo</generator>
            <ttl>60</ttl>
							                    <item>
                        <title>The 12-Hour Biological Rhythm</title>
                        <link>https://cyclesresearchinstitute.org/community/biology-human-biology/the-12-hour-biological-rhythm/</link>
                        <pubDate>Tue, 01 Sep 2026 21:05:39 +0000</pubDate>
                        <description><![CDATA[The 12-Hour Biological Rhythm
Internal Clocks: Chronobiologists have discovered that thousands of genes and physiological processes—such as core body temperature, hormone levels, blood pres...]]></description>
                        <content:encoded><![CDATA[<p>The 12-Hour Biological Rhythm</p>
<p>Internal Clocks: Chronobiologists have discovered that thousands of genes and physiological processes—such as core body temperature, hormone levels, blood pressure, and alertness—follow a dual oscillation, peaking and dipping every 12 hours rather than just once a day. Biological "Rush Hours": This 12-hour cycle creates secondary peaks of metabolic and genetic activity around dawn and dusk, helping the body manage daily shifts in energy demands.The Post-Lunch Dip: The minor drop in alertness and body temperature that humans typically feel about 12 hours after the middle of the night (usually early-to-mid afternoon) is a direct reflection of this internal rhythm interacting with our sleep-wake cycle. Midday Naps in Past and Traditional Societies.</p>
<p>The Siesta Tradition: Across Mediterranean, Middle Eastern, Latin American, and parts of Asian history, a post-midday rest or siesta was standard practice.Beating the Heat: Societies naturally paused work during the hottest hours of the afternoon when ambient temperatures peaked and physical labor became inefficient or dangerous. Biphasic Sleep: This habit forms part of a biphasic sleep pattern (sleeping in two distinct daily blocks, such as a shortened night plus a daytime nap), which was widely practiced before industrial work schedules enforced a single, continuous 8-hour block of night sleep.</p>]]></content:encoded>
						                            <category domain="https://cyclesresearchinstitute.org/community/"></category>                        <dc:creator>RayTomes</dc:creator>
                        <guid isPermaLink="true">https://cyclesresearchinstitute.org/community/biology-human-biology/the-12-hour-biological-rhythm/</guid>
                    </item>
				                    <item>
                        <title>The Potato That Could Forecast the Weather: Frank A. Brown Jr.&#039;s Exogenous Clock</title>
                        <link>https://cyclesresearchinstitute.org/community/biology-human-biology/the-potato-that-could-forecast-the-weather-frank-a-brown-jr-s-exogenous-clock/</link>
                        <pubDate>Mon, 31 Aug 2026 18:44:50 +0000</pubDate>
                        <description><![CDATA[The Potato That Could Forecast the Weather: Frank A. Brown Jr.&#039;s Exogenous Clock
If you&#039;ve spent any time in cycles research, you&#039;ve run into the standard story of biological rhythms: every...]]></description>
                        <content:encoded><![CDATA[<h1>The Potato That Could Forecast the Weather: Frank A. Brown Jr.'s Exogenous Clock</h1>
<p>If you've spent any time in cycles research, you've run into the standard story of biological rhythms: every organism carries its own internal clock, a self-winding mechanism ticking away independent of the outside world. Through the 1950s and 60s, one Northwestern University biologist quietly built a career-long case that this story was, at minimum, incomplete.</p>
<p>Frank A. Brown Jr., Morrison Professor of Biological Sciences at Northwestern and a Consultant at the Marine Biological Laboratory in Woods Hole, wasn't a fringe figure. He was Vice President of the American Society of Zoologists, President of the Society of General Physiologists, and — worth noting for this community — an elected Director of the Foundation for the Study of Cycles from 1960 onward. His papers ran in <em>PNAS</em>, <em>Biological Bulletin</em>, and the <em>American Journal of Physiology</em>. And his experiments were genuinely strange.</p>
<p><strong>The fiddler crab.</strong> Brown's earliest work showed that fiddler crabs, which darken and lighten daily via pigment movement, kept perfect 24-hour rhythm even sealed in total darkness at 26°C, 16°C, or 6°C — as if something outside the box was still reaching them.</p>
<p><strong>The potato that predicted pressure.</strong> This is the one that made the New York Times (April 17, 1959). Brown sealed pieces of potato — no nervous system, so no behavioral tricks possible — in hermetically closed, constant-condition chambers and tracked their oxygen consumption continuously for over two years. Two results stood out: a lunar-monthly rhythm in metabolic rate (lowest at new moon, peaking at third quarter), and — stranger still — a metabolic rate that tracked barometric pressure roughly <em>two days before</em> that pressure actually arrived. Salamanders in a parallel study showed the same anticipatory pattern.</p>
<p><strong>The oysters that moved their tides inland.</strong> Maybe the best story of all. Brown took oysters from New Haven, Connecticut — where they naturally open their shells at high tide — and shipped them to landlocked Evanston, Illinois. Over time, the oysters shifted their opening schedule to match the high tide Evanston <em>would</em> have, if Evanston were on the coast. No tide, no obvious cue — just a slow drift toward a rhythm keyed to a force nobody could point to directly.</p>
<p><strong>Magnetism, into the '60s.</strong> By 1962, working with geologist Karl C. Hammer, Brown reported that snails, worms, and the single-celled paramecium could detect magnetic fields, and that the planarian worm <em>Dugesia</em> even distinguished north from south poles. By 1964 he was writing directly for Cycles Magazine on the topic ("How Animals Respond To Magnetism"), and the Foundation was floating the idea that regular fluctuations in the earth's magnetic field might be a hidden driver behind some of the cycles the FSC had been cataloguing for years.</p>
<p><strong>The jet-lag note.</strong> Almost in passing, Cycles (March 1963) cited Brown's observation that a person flown from California to England keeps their body clock — waking pattern, temperature, blood cell counts, hormones — running on California time for eight to ten days before it resets. This is, essentially, an early clinical description of jet lag, years before the term existed.</p>
<p>Brown's proposed mechanism — that organisms are entrained by very weak geophysical signals like barometric pressure, cosmic radiation, and magnetism, rather than running on a purely self-contained clock — lost out to the now-dominant endogenous-clock model in chronobiology. But he wasn't wrong that something was going on in his data, and he was never a crank: credentialed, published, elected. For anyone interested in the roads not taken in cycle science, he's a genuinely interesting figure to dig into further.</p>
<p><em>Sourced from Cycles Magazine, 1959–1964 (Vols. 10–15), Foundation for the Study of Cycles.</em></p>]]></content:encoded>
						                            <category domain="https://cyclesresearchinstitute.org/community/"></category>                        <dc:creator>RayTomes</dc:creator>
                        <guid isPermaLink="true">https://cyclesresearchinstitute.org/community/biology-human-biology/the-potato-that-could-forecast-the-weather-frank-a-brown-jr-s-exogenous-clock/</guid>
                    </item>
				                    <item>
                        <title>The 20-year yield hits the cycle model&#039;s loudest topping alarm just as Warsh argues rates higher</title>
                        <link>https://cyclesresearchinstitute.org/community/forecasting/the-20-year-yield-hits-the-cycle-models-loudest-topping-alarm-just-as-warsh-argues-rates-higher/</link>
                        <pubDate>Mon, 31 Aug 2026 10:05:01 +0000</pubDate>
                        <description><![CDATA[The 20-year yield hits the cycle model&#039;s loudest topping alarm just as Warsh argues rates higher - Lar von Thienen August 30 (shown with permission)
Friday belonged to Warsh. The Fed chair ...]]></description>
                        <content:encoded><![CDATA[<h1 class="post-title published title-X77sOw" dir="auto">The 20-year yield hits the cycle model's loudest topping alarm just as Warsh argues rates higher <span style="font-size: 14pt">- Lar von Thienen August 30 (shown with permission)</span></h1>
<p>Friday belonged to Warsh. The Fed chair used his Jackson Hole speech to praise the labor market and to make clear that better inflation prints have not convinced him the underlying trend has improved, and the market took the hint. Pricing moved to 37 basis points of hikes for the rest of the year, up from 27 before the speech, with the odds of a September 16 move now near 57%. Yields rose across the curve, the Fed-sensitive 2-year spiking 12 basis points while 10- and 30-year yields added 5 and 2. Equities slipped in response, the S&amp;P 500 off 0.25%, the Nasdaq down 0.52%, the Russell 2000 hit hardest at minus 1.39%, the Dow flat. The dollar index gained half a percent. The sharpest moves sat in the metals, where gold fell 3% and silver 4%, while wheat jumped another 3% to cap a 12% week as the Russia-Ukraine war escalated, and Brent eased 0.4% to around $89.30.</p>
<p><span>The scan answers with its deepest possible reading on exactly the market Warsh moved. The day’s thesis is that the long end of the bond market has run into a maximum-strength topping warning, a reading that the rise in yields could stall or reverse, while the Fed chair argues for higher rates, and one of the two sides has to give. The 20-year Treasury yield reaches the bearish maximum of -100 with a bull exit, the buyers step back and the climb no longer carries, and the 30-year escalates to -87 with a bull exit of its own. Both readings stand on data through Thursday’s close, so Friday’s yield rise ran into a freshly hardened alert, the same confrontation the rates call has carried since it </span><a href="https://substack.com/redirect/f2fea834-93c8-41e6-b016-1ac405571b58?j=eyJ1Ijoib3g5MjAifQ.AiRUNY1YoOgQftTDDxF2R1EdQZCNduaaXKUtuBb9Wds">escalated in the August 22 review</a><span> and </span><a href="https://substack.com/redirect/60a23d79-f484-46d0-9afe-019f829a92dc?j=eyJ1Ijoib3g5MjAifQ.AiRUNY1YoOgQftTDDxF2R1EdQZCNduaaXKUtuBb9Wds">moved to the center of the scan on August 26</a><span>. The second thread sits in the metals, where the model had already stepped down from most of the hard-asset wall it </span><a href="https://substack.com/redirect/96898a69-f63c-4d59-8cb7-569b3d970233?j=eyJ1Ijoib3g5MjAifQ.AiRUNY1YoOgQftTDDxF2R1EdQZCNduaaXKUtuBb9Wds">flagged on August 5</a><span> and </span><a href="https://substack.com/redirect/1a15d330-649e-4a22-87e4-284001a21f44?j=eyJ1Ijoib3g5MjAifQ.AiRUNY1YoOgQftTDDxF2R1EdQZCNduaaXKUtuBb9Wds">confirmed on August 9</a><span>. Silver and the miners dropped off the model’s alert list over the past two weeks, so Friday’s 3 to 4% metal slide landed on positions the model had normalized, and what remains under test is gold’s six-cycle floor.</span></p>
<p>Our daily analysis filters roughly 45 markets through a cycle consensus engine. Each asset receives a Consensus Score from -100 to +100, the model’s summary reading for a possible turn, where positive values mark potential cyclical bottoms (a time window where a decline could end) and negative values potential cyclical tops. Readings beyond ±60 enter the critical zone, a critical reading at which the cycle model raises the alarm for a possible turn. Let’s take a closer look.</p>]]></content:encoded>
						                            <category domain="https://cyclesresearchinstitute.org/community/"></category>                        <dc:creator>RayTomes</dc:creator>
                        <guid isPermaLink="true">https://cyclesresearchinstitute.org/community/forecasting/the-20-year-yield-hits-the-cycle-models-loudest-topping-alarm-just-as-warsh-argues-rates-higher/</guid>
                    </item>
				                    <item>
                        <title>Luke Tomes</title>
                        <link>https://cyclesresearchinstitute.org/community/intro/luke-tomes/</link>
                        <pubDate>Mon, 31 Aug 2026 01:42:34 +0000</pubDate>
                        <description><![CDATA[Kia ora everyone, I’m Luke Tomes (Ray&#039;s Son), a 52-year-old music producer, audio engineer and technology entrepreneur based in New Zealand. My career has taken me from producing and enginee...]]></description>
                        <content:encoded><![CDATA[<p><span>Kia ora everyone, I’m Luke Tomes (Ray's Son), a 52-year-old music producer, audio engineer and technology entrepreneur based in New Zealand. My career has taken me from producing and engineering music for artists including Stellar*, Bic Runga and Pluto to building software products and leading technology teams.</span></p>
<p><span>I helped start </span><a href="https://www.inquisitive.com/"><span>Inquisitive</span></a><span>, an education platform, and </span><a href="https://www.voiceq.com/"><span>VoiceQ</span></a><span>, dubbing and dialogue-recording software for film and television. My background spans product design, technology strategy and project management, with a practical focus on turning ideas into useful tools.</span></p>
<p><span>I work with Ray Tomes at CRI, supporting his cycles and harmonics research on the technology and project-management side. This includes helping develop research tools, organise projects and make the work more accessible to others. Coming from music and audio, I’m particularly interested in the connections between the harmonics theory and music, sound synthesis and rhythm, harmonics and patterns in the natural world.</span></p>
<p>Definitely keen to investigate the WSM further, although my maths and physic skills and knowledge are not up to the level of my Dads.</p>
<p><span>I’m looking forward to exchanging ideas, learning from others and contributing to the CRI community.</span></p>]]></content:encoded>
						                            <category domain="https://cyclesresearchinstitute.org/community/"></category>                        <dc:creator>Luke Tomes</dc:creator>
                        <guid isPermaLink="true">https://cyclesresearchinstitute.org/community/intro/luke-tomes/</guid>
                    </item>
				                    <item>
                        <title>The 26–27-Million-Year Cycle of Extinctions etc</title>
                        <link>https://cyclesresearchinstitute.org/community/geology-paleontology/the-26-27-million-year-cycle-of-extinctions-etc/</link>
                        <pubDate>Mon, 31 Aug 2026 00:33:04 +0000</pubDate>
                        <description><![CDATA[The 26–27-Million-Year Cycle: Extinctions, Volcanism, and a Contested Cause
Overview
Where the 2.4 Myr and 405 kyr eccentricity cycles are grounded in exact celestial mechanics, the 26–27 ...]]></description>
                        <content:encoded><![CDATA[<h1 class="western">The 26–27-Million-Year Cycle: Extinctions, Volcanism, and a Contested Cause</h1>
<h2 class="western">Overview</h2>
<p>Where the 2.4 Myr and 405 kyr eccentricity cycles are grounded in exact celestial mechanics, the <b>26–27 Myr cycle</b> sits at the opposite end of the evidentiary spectrum: a periodicity detected purely statistically, in the timing of mass extinctions and other major geological events, whose <i>cause</i> remains genuinely unresolved after more than four decades of research. It is also one of the best-tested — repeatedly re-run against revised geological timescales, richer datasets, and independent event categories, and it keeps surviving.</p>
<h2 class="western">The original discovery</h2>
<p>The signal was first identified by David Raup and John Sepkoski in 1984. Analyzing the extinction intensity of marine animal families over the preceding 250 million years, they found 12 extinction events showing statistically significant periodicity (P &lt; 0.01), with a mean interval of <b>26 million years</b> between them — a finding immediately controversial because two of the events lined up with the terminal-Cretaceous and Late Eocene extinctions already tied to meteorite impacts. This raised the "Nemesis" hypothesis: that the Sun might have an undetected companion star in a wide, eccentric orbit, periodically disturbing the Oort Cloud and showering the inner solar system with comets.</p>
<p>The finding was attacked on statistical grounds almost immediately. Stigler and Wagner argued that the coarse resolution of the 1980s geological timescale — with stage boundaries themselves spaced fairly regularly — could manufacture an apparent periodicity out of essentially random extinction timing. Raup and Sepkoski countered with genus-level data (finer-resolution than families) and continued to find the same 26 Myr signal, though they acknowledged the debate needed more data to settle.</p>
<h2 class="western">Re-confirmation with better data: 26 → 27 Myr</h2>
<p>That better data arrived in two major waves.</p>
<p><b>Melott &amp; Bambach (2010)</b> reanalyzed both the Sepkoski genus compendium and the newer Paleobiology Database, using the revised 2004 geological timescale, and extended the record back to <b>500 million years</b> — twice as far as the original study. They found a periodicity of <b>27 million years</b>, confirmed at 99% confidence, with the shift from 26 to 27 Myr essentially just a bookkeeping consequence of the geological timescale itself having grown about 3% longer since 1984. Notably, they used the increased regularity of the signal to argue <i>against</i> the Nemesis hypothesis: a real companion star's orbit would have been perturbed by the many stellar encounters the Sun has had over 500 million years, which should smear the periodicity out — yet the interval stayed metronomically regular, which better fits a mechanism more stable than a distant, gravitationally jostled star.</p>
<p><b>Melott &amp; Bambach (2013)</b>, following the 2012 revision of the international geological timescale, repeated the analysis again with even finer taxonomic resolution (genera rather than families). The 27 Myr periodicity not only survived but came through with <i>improved</i> statistical significance, and the excess of extinction events lining up with the periodicity's predicted maxima was confirmed at the p ≈ 0.01–0.02 level across the full Phanerozoic.</p>
<h2 class="western">Beyond extinctions: a broader geological pulse</h2>
<p>Independently, Michael Rampino and collaborators have spent over three decades documenting a closely related periodicity — usually reported as 26–30 Myr — not just in extinctions but across a much wider range of geological phenomena: continental flood-basalt eruptions, major plate-tectonic reorganizations, ocean-anoxic events, sea-level fluctuations, intraplate magmatism, and marine strontium-isotope excursions. A 2026 synthesis by Rampino compiling 89 major geological events over the last 260 million years found them clustering into 10 peaks spaced roughly <b>26 million years</b> apart — extinctions, flood basalts, and anoxic events tending to co-occur within the same peaks rather than scattering independently, which is itself a striking result: it suggests a single underlying pacer rather than several coincidentally similar but separate cycles. The most recent estimate from this broader dataset, released in 2026, puts the period at <b>27.5 million years</b>, with a much weaker secondary signal near 8.9 Myr.</p>
<h2 class="western">The unresolved question: what causes it?</h2>
<p>Unlike the 2.4 Myr and 405 kyr cycles, no candidate mechanism for the 26–27 Myr cycle has anything like a settled physical derivation. Three broad classes of explanation remain in competition:</p>
<ol>
<li>
<p><b>Internal Earth processes.</b> Deep-mantle plume cycles or convective overturns could plausibly drive periodic flood-basalt volcanism and associated extinctions on multi-million-year timescales, without needing any astronomical trigger at all.</p>
</li>
<li>
<p><b>Galactic-plane crossings.</b> As the solar system orbits the galactic center, it periodically passes through the crowded mid-plane of the Milky Way's disc, which could gravitationally perturb the Oort Cloud and increase cometary bombardment — a revival, in modified form, of the old Nemesis idea, but driven by galactic structure (including, in some versions, a hypothesized thin disc of dark matter) rather than a companion star. Rampino and others have shown correlations between impact-crater ages, extinction pulses, flood basalts, and estimated galactic-plane-crossing times, with roughly 6 of 13 proposed impact pulses over 260 Myr lining up with plane crossings in one widely used model (Randall &amp; Reece 2014).</p>
</li>
<li>
<p><b>Very long-period orbital/climatic forcing on the crust.</b> A more speculative proposal is that long-term redistribution of water, ice, and sediment via slow orbital or climatic cycles subtly alters crustal and mantle stresses, encouraging episodic tectonic and volcanic activity — an indirect, climate-mediated route rather than a direct astronomical trigger.</p>
</li>
</ol>
<p>None of these has been confirmed, and Rampino's own recent framing treats the question as explicitly open, with the resolution likely to depend on tightening the radiometric dates of the largest flood basalts and impact craters — some of which already carry uncertainties at or below a million years, small enough that a few more precise dates could start to discriminate between the competing mechanisms.</p>
<h2 class="western">Cross-check against your own analysis</h2>
<p>This is one of the few long cycles where independent, modern statistical methods keep landing on essentially the same number from essentially the same class of data: Raup &amp; Sepkoski's original 26 Myr, Melott &amp; Bambach's 27 Myr (2010, reconfirmed 2013), and Rampino's 26–27.5 Myr figure from a much broader, non-extinction-only event set. Your own CATS run on the Puetz marine-genera dataset returned 27.15 Myr, and an independent Lomb-Scargle periodogram on the same raw data reproduced it almost exactly (27.15 Myr) — sitting right in the middle of this now four-decade-long, repeatedly-revised literature consensus.</p>
<h2 class="western">Key sources</h2>
<ul>
<li>
<p>Raup, D.M. &amp; Sepkoski, J.J. (1984). <i>Periodicity of extinctions in the geologic past.</i> PNAS 81, 801–805.</p>
</li>
<li>
<p>Melott, A.L. &amp; Bambach, R.K. (2010). <i>Nemesis Reconsidered.</i> MNRAS Letters 407, L99–L102.</p>
</li>
<li>
<p>Melott, A.L. &amp; Bambach, R.K. (2013). <i>Do periodicities in extinction — with possible astronomical connections — survive a revision of the geological timescale?</i> ApJ 773, 6.</p>
</li>
<li>
<p>Rampino, M.R. &amp; Caldeira, K.; Rampino, M.R. et al. (2019, 2021a, 2021b) — series of papers on ~26–30 Myr periodicity across flood basalts, anoxic events, sea-level change, and intraplate magmatism.</p>
</li>
<li>
<p>Rampino, M.R. (2015). <i>Disc dark matter in the Galaxy and potential cycles of extraterrestrial impacts, mass extinctions and geological events.</i> MNRAS 448, 1816.</p>
</li>
<li>
<p>Randall, L. &amp; Reece, M. (2014). Galactic-plane-crossing model referenced in Rampino's impact/extinction correlation work.</p>
</li>
<li>
<p>Rampino, M.R. (2026). <i>The temporal characteristics of the geologic record: Global correlations and similar multi-million-year cycles of major geologic events, with potential internal-Earth versus astronomical causes</i> — 89-event, 260 Myr synthesis giving 27.5 Myr with a secondary 8.9 Myr signal.</p>
</li>
</ul>
<p>&nbsp;</p>]]></content:encoded>
						                            <category domain="https://cyclesresearchinstitute.org/community/"></category>                        <dc:creator>RayTomes</dc:creator>
                        <guid isPermaLink="true">https://cyclesresearchinstitute.org/community/geology-paleontology/the-26-27-million-year-cycle-of-extinctions-etc/</guid>
                    </item>
				                    <item>
                        <title>2.4 Million and 405 thousand year cycles and the planets</title>
                        <link>https://cyclesresearchinstitute.org/community/hsa-sub-forum-1/2-4-million-and-405-thousand-year-cycles-and-the-planets/</link>
                        <pubDate>Mon, 31 Aug 2026 00:26:02 +0000</pubDate>
                        <description><![CDATA[The 2.4-Million-Year Eccentricity Grand Cycle — and Its Relationship to the 405-kyr &quot;Metronome&quot;
Overview
Of all the long geological cycles surveyed for periods over a million years, the 2....]]></description>
                        <content:encoded><![CDATA[<h1 class="western">The 2.4-Million-Year Eccentricity Grand Cycle — and Its Relationship to the 405-kyr "Metronome"</h1>
<h2 class="western">Overview</h2>
<p>Of all the long geological cycles surveyed for periods over a million years, the <b>2.4 Myr eccentricity grand cycle</b> stands apart: it is the one whose mechanism is understood exactly, from first principles, rather than inferred statistically from a fossil or isotope record. It is a direct product of gravity — the mutual perturbation of Earth's orbit by Mars and Jupiter — and it is inseparable from a shorter, better-known relative: the <b>405 kyr eccentricity cycle</b>, widely called the astrochronological "metronome." Understanding the pair together, rather than as two unrelated numbers, is the point of this article.</p>
<h2 class="western">The 405 kyr cycle: the "metronome"</h2>
<p>Earth's orbital eccentricity — how far its orbit departs from a circle — oscillates on several superimposed periods. By far the strongest single term in that oscillation, in the recent geological past, has a period of about 405,000 years. In the formal notation of celestial mechanics this is the <b>g₂ − g₅</b> term: the beat frequency between the precession rates of Venus's and Jupiter's orbits, which couples into Earth's eccentricity through the planets' mutual gravitational tugging.</p>
<p>What makes 405 kyr special for geologists is not just its strength but its <b>stability</b>. Unlike the shorter eccentricity, obliquity, and precession cycles (100 kyr, 41 kyr, 21–23 kyr), which drift and interact chaotically over tens of millions of years and become essentially impossible to calculate precisely beyond about 50 Ma, the g₂ − g₅ term is dominated by Jupiter and Saturn — the two largest, most dynamically "stiff" bodies in the solar system, least affected by chaotic diffusion. Jacques Laskar's long-term astronomical solutions (La2004, La2010) show this term holding its ~405 kyr period with very little drift across the full 250 Myr they modeled, and empirical work — notably Kent et al. (2018, PNAS), anchoring the Newark Basin rift-lake sediment cycles (the "McLaughlin cycles") to radiometrically dated ash beds — confirmed the 405 kyr period had barely changed over roughly 200 million years.</p>
<p>This stability has made the 405 kyr cycle the backbone of <b>astrochronology</b>: geologists count these cycles, layer by layer, through sedimentary sequences to build absolute-age timescales far more precise than radiometric dating alone allows, especially through the Mesozoic. It has now been identified in settings as varied as Triassic–Jurassic Newark Basin lake beds, Eocene coal measures in China, and Ordovician marine sequences 469 million years old, where its appearance coincides with the start of the Great Ordovician Biodiversification Event.</p>
<h2 class="western">The 2.4 Myr grand cycle</h2>
<p>The 405 kyr term does not stand alone; its amplitude itself waxes and wanes on a longer envelope. This is the <b>g₄ − g₃</b> term — the beat between the perihelion-precession rates of Mars and Earth — with a period of roughly 2.4 million years. Rather than being a separate, independent oscillation, it modulates the strength of the shorter eccentricity terms: during a 2.4 Myr amplitude maximum, Earth's orbit swings through much larger eccentricity extremes (and the 405 kyr signal shows up more strongly in sediment); during an amplitude minimum, eccentricity stays closer to circular and the shorter cycles are muted.</p>
<p>This 2.4 Myr signal has been found directly in the rock and sediment record:</p>
<ul>
<li>
<p>A 2024 <i>Nature Communications</i> study (Dutkiewicz, Boulila &amp; Müller) found a clear ~2.4 Myr signal in the spacing of Cenozoic deep-sea sedimentary hiatuses — gaps caused by intensified deep-ocean bottom currents, which the authors link to eccentricity maxima strengthening seasonal contrasts and ocean circulation. The signal is disrupted around 56 million years ago, at the Paleocene–Eocene Thermal Maximum, coinciding with a known chaotic transition in the inner solar system's orbital configuration.</p>
</li>
<li>
<p>Extraordinarily, the same underlying cyclicity has been read out of <b>2.46-billion-year-old banded iron formations</b>, using the ratio of shorter precession-scale cycles nested within each eccentricity cycle to reconstruct how fast Earth was spinning and how close the Moon was at the time — the oldest reliable astronomically-derived geological date currently available.</p>
</li>
</ul>
<p>A companion term, <b>s₄ − s₃</b>, produces a related ~1.2 Myr cycle in orbital inclination (not eccentricity), and the near 2:1 relationship between the 2.4 Myr and 1.2 Myr terms is itself a recognized driver of the solar system's long-term chaotic behavior.</p>
<h2 class="western">How they relate</h2>
<p>So the two numbers are not coincidentally close in ratio — 2.4 Myr / 405 kyr ≈ 5.9, close to a 6:1 relationship — they are genuinely nested: the 405 kyr term is the fast "tick," and the 2.4 Myr term is the slow envelope that periodically amplifies and suppresses that tick. Both come from the same family of planetary secular-resonance terms, just involving different pairs of planets (Venus–Jupiter for the 405 kyr; Mars–Earth for the 2.4 Myr). In sediment records this is exactly how they present: cyclostratigraphers look for 405 kyr bundles that themselves group into packages of five to six, forming the longer 2.4 Myr envelope — a pattern documented in Triassic Newark Basin cores and elsewhere.</p>
<h2 class="western">An important recent complication</h2>
<p>Not everything about the 405 kyr "metronome" is as settled as the term implies. A 2024 paper (IOPscience, <i>A Secular Solar System Resonance that Disrupts the Dominant Cycle in Earth's Orbital Eccentricity</i>) found that in roughly 40% of plausible solar system solutions, a secular resonance destabilizes the g₂ − g₅ term over deep time, weakening or shifting the 405 kyr period. This directly challenges the long-standing assumption — used throughout deep-time cyclostratigraphy — that 405 kyr can be treated as fixed far beyond 50 Ma. It doesn't overturn the empirical Newark Basin result (which is a direct measurement, not a model), but it's a live caveat: the theoretical case for extending the "metronome" assumption hundreds of millions of years further back than it has actually been empirically checked is weaker than the confident tone of most cyclostratigraphy papers suggests.</p>
<h2 class="western">Why it matters</h2>
<ul>
<li>
<p>It is the most rigorously <i>derived</i> (rather than statistically <i>detected</i>) long cycle on record — a useful benchmark against which more speculative multi-million-year periodicities (26–27 Myr extinction pulses, 62 and 140 Myr biodiversity cycles, and so on) can be compared, since none of those has anything close to this mechanistic grounding.</p>
</li>
<li>
<p>It anchors the entire practice of astrochronology, which is how much of the Mesozoic and early Cenozoic timescale gets its absolute dates today.</p>
</li>
<li>
<p>Its confirmed presence in 2.46-billion-year-old rock shows that, mechanism aside, this particular clock has been ticking — with the same gears — for essentially the whole of Earth's geological history.</p>
</li>
</ul>
<h2 class="western">Key sources</h2>
<ul>
<li>
<p>Laskar, J. et al. (2004). <i>A long-term numerical solution for the insolation quantities of the Earth.</i> Astronomy &amp; Astrophysics 428, 261–285.</p>
</li>
<li>
<p>Kent, D.V. et al. (2018). <i>Empirical evidence for stability of the 405-kiloyear Jupiter–Venus eccentricity cycle over hundreds of millions of years.</i> PNAS 115, 6153–6158.</p>
</li>
<li>
<p>Dutkiewicz, A., Boulila, S. &amp; Müller, R.D. (2024). <i>Deep-sea hiatus record reveals orbital pacing by 2.4 Myr eccentricity grand cycles.</i> Nature Communications.</p>
</li>
<li>
<p>Olsen, P.E. et al. — Milankovitch cycles in 2.46-billion-year-old banded iron formations (PMC).</p>
</li>
<li>
<p>Kane, S.R., Vervoort, P. &amp; Horner, J. (2025). <i>The Dependence of Earth Milankovitch Cycles on Martian Mass.</i> IOPscience.</p>
</li>
<li>
<p>IOPscience (2024). <i>A Secular Solar System Resonance that Disrupts the Dominant Cycle in Earth's Orbital Eccentricity.</i></p>
</li>
<li>
<p>Middle Ordovician astrochronology study linking the 405 kyr cycle to the Great Ordovician Biodiversification Event (PMC).</p>
</li>
</ul>
<p>&nbsp;</p>]]></content:encoded>
						                            <category domain="https://cyclesresearchinstitute.org/community/"></category>                        <dc:creator>RayTomes</dc:creator>
                        <guid isPermaLink="true">https://cyclesresearchinstitute.org/community/hsa-sub-forum-1/2-4-million-and-405-thousand-year-cycles-and-the-planets/</guid>
                    </item>
				                    <item>
                        <title>Best and Latest in the Forum - late August</title>
                        <link>https://cyclesresearchinstitute.org/community/announcements-please-read-this-first/best-and-latest-in-the-forum-late-august/</link>
                        <pubDate>Sun, 30 Aug 2026 08:03:07 +0000</pubDate>
                        <description><![CDATA[A very useful App for cycles researchers
Another very useful App]]></description>
                        <content:encoded><![CDATA[<ol>
<li>A very useful App for cycles researchers <a title="Kotov&apos;s Method" href="https://cyclesresearchinstitute.org/community/methods/kotovs-method-app/" target="_blank" rel="noopener">https://cyclesresearchinstitute.org/community/methods/kotovs-method-app/</a></li>
<li>Another very useful App <a title="Graph Digitizer" href="https://cyclesresearchinstitute.org/community/methods/graph-digitizer/" target="_blank" rel="noopener">https://cyclesresearchinstitute.org/community/methods/graph-digitizer/</a></li>
</ol>]]></content:encoded>
						                            <category domain="https://cyclesresearchinstitute.org/community/"></category>                        <dc:creator>RayTomes</dc:creator>
                        <guid isPermaLink="true">https://cyclesresearchinstitute.org/community/announcements-please-read-this-first/best-and-latest-in-the-forum-late-august/</guid>
                    </item>
				                    <item>
                        <title>Graph Digitizer</title>
                        <link>https://cyclesresearchinstitute.org/community/methods/graph-digitizer/</link>
                        <pubDate>Sun, 30 Aug 2026 07:52:18 +0000</pubDate>
                        <description><![CDATA[Quite often I find graphs published in articles but I can not find the data that the graph is based on. It can be very frustrating. So I made this app to allow the graphic to be dropped into...]]></description>
                        <content:encoded><![CDATA[<p>Quite often I find graphs published in articles but I can not find the data that the graph is based on. It can be very frustrating. So I made this app to allow the graphic to be dropped into it and after a few clicks on the scales and such it gives you a CSV file of the data in the graph. Here are the steps to follow:</p>
<ol>
<li>Copy the code below into your browser (or save and click to load).</li>
<li>Load the image by browsing to it.</li>
<li>Calibrate the x axis (usually time) by clicking each of an early and late date/time and specifying the value at that point.</li>
<li>If the y axis is on a log scale click that button.</li>
<li>Calibrate the y axis by clicking each of a low vale and a high value on the scale and typing in those values. </li>
<li>Give the series a name.</li>
<li>Select the colour with the cross hairs. Can be delicate for thin lines but it shows what you selected.  If you miss you can try again.</li>
<li>Give it the lower left and upper right corners that the graph stays inside. Make sure that there are no other things of that colour in that area or they will get digitized too.</li>
<li>Press extract curve.</li>
<li>Copy the resulting CSV file which has the co-ordinates of all the x,y points along the graph.</li>
<li>Go paste it somewhere useful.</li>
</ol>
<p>Thanks Claude for your help.</p>
<pre contenteditable="false">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
&lt;meta charset="UTF-8"&gt;
&lt;title&gt;Graph Digitizer&lt;/title&gt;
&lt;style&gt;
  :root{
    --bg: #14181c;
    --panel: #1c2126;
    --panel-2: #232a30;
    --line: #313a41;
    --text: #e7ecf0;
    --muted: #8b98a3;
    --accent: #5fd3c4;
    --accent-dim: #3a7d74;
    --warn: #e8a33d;
    --danger: #e0665f;
    --mono: ui-monospace, "SF Mono", "Cascadia Code", "JetBrains Mono", Menlo, Consolas, monospace;
    --sans: -apple-system, "Segoe UI", ui-sans-serif, system-ui, sans-serif;
  }
  *{box-sizing:border-box;}
  html,body{margin:0;padding:0;background:var(--bg);color:var(--text);font-family:var(--sans);}
  body{padding:20px;}
  h1{font-size:15px;letter-spacing:.08em;text-transform:uppercase;color:var(--accent);margin:0 0 2px;font-weight:600;}
  .sub{color:var(--muted);font-size:12.5px;margin:0 0 18px;line-height:1.5;}
  .layout{display:grid;grid-template-columns:300px 1fr 320px;gap:16px;align-items:start;}
  @media (max-width:1100px){.layout{grid-template-columns:1fr;}}
  .panel{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:14px;}
  .panel + .panel{margin-top:14px;}
  .step{border-left:2px solid var(--line);padding:2px 0 14px 14px;position:relative;}
  .step:last-child{padding-bottom:2px;}
  .step::before{content:attr(data-n);position:absolute;left:-11px;top:0;width:20px;height:20px;border-radius:50%;background:var(--panel-2);border:1px solid var(--line);color:var(--muted);font-family:var(--mono);font-size:10.5px;display:flex;align-items:center;justify-content:center;}
  .step.active::before{border-color:var(--accent);color:var(--accent);background:#132523;}
  .step.done::before{content:"?";border-color:var(--accent-dim);color:var(--accent);}
  .step h3{margin:0 0 6px;font-size:12.5px;color:var(--text);font-weight:600;}
  .step p.hint{margin:0 0 8px;font-size:11.5px;color:var(--muted);line-height:1.5;}
  button{font-family:var(--sans);font-size:12.5px;background:var(--panel-2);color:var(--text);border:1px solid var(--line);padding:7px 11px;border-radius:6px;cursor:pointer;}
  button:hover{border-color:var(--accent-dim);}
  button:focus-visible{outline:2px solid var(--accent);outline-offset:1px;}
  button.primary{background:var(--accent);color:#0c1214;border-color:var(--accent);font-weight:600;}
  button.primary:hover{background:#7ee0d3;}
  button:disabled{opacity:.35;cursor:not-allowed;}
  button.small{padding:4px 8px;font-size:11px;}
  input,input{font-family:var(--mono);font-size:12.5px;background:var(--panel-2);color:var(--text);border:1px solid var(--line);border-radius:5px;padding:6px 8px;width:100%;}
  input:focus-visible{outline:2px solid var(--accent);outline-offset:1px;}
  label{font-size:11px;color:var(--muted);display:block;margin-bottom:3px;}
  .row{display:flex;gap:8px;margin-bottom:8px;}
  .row &gt; div{flex:1;}
  .canvas-wrap{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:12px;position:relative;overflow:auto;max-height:78vh;}
  .canvas-inner{position:relative;display:inline-block;}
  canvas{display:block;max-width:100%;cursor:crosshair;border-radius:2px;}
  .readout{position:absolute;pointer-events:none;font-family:var(--mono);font-size:10.5px;background:rgba(12,18,20,.88);color:var(--accent);border:1px solid var(--accent-dim);padding:3px 6px;border-radius:4px;white-space:nowrap;transform:translate(10px,-28px);}
  .drop{border:1.5px dashed var(--line);border-radius:8px;padding:28px 10px;text-align:center;color:var(--muted);font-size:12px;cursor:pointer;}
  .drop:hover{border-color:var(--accent-dim);color:var(--text);}
  .series-item{border:1px solid var(--line);border-radius:6px;padding:8px;margin-bottom:8px;background:var(--panel-2);}
  .series-item.active{border-color:var(--accent);}
  .swatch{width:13px;height:13px;border-radius:3px;display:inline-block;vertical-align:-2px;border:1px solid rgba(255,255,255,.2);margin-right:6px;}
  .series-name{font-size:12.5px;font-weight:600;}
  .series-meta{font-size:10.5px;color:var(--muted);font-family:var(--mono);margin-top:3px;}
  .tabs{display:flex;gap:4px;margin-bottom:10px;}
  .tabs button{flex:1;}
  .tabs button.active{background:var(--panel-2);border-color:var(--accent);color:var(--accent);}
  table{width:100%;border-collapse:collapse;font-family:var(--mono);font-size:11px;}
  th,td{text-align:left;padding:3px 6px;border-bottom:1px solid var(--line);}
  th{color:var(--muted);font-weight:500;position:sticky;top:0;background:var(--panel);}
  .tablewrap{max-height:220px;overflow:auto;border:1px solid var(--line);border-radius:6px;margin-top:6px;}
  textarea{width:100%;height:140px;font-family:var(--mono);font-size:11px;background:var(--panel-2);color:var(--text);border:1px solid var(--line);border-radius:6px;padding:8px;resize:vertical;}
  .flexgap{display:flex;gap:8px;margin-top:8px;}
  .flexgap button{flex:1;}
  .empty{color:var(--muted);font-size:12px;text-align:center;padding:20px 6px;}
  .checkrow{display:flex;align-items:center;gap:6px;font-size:11.5px;color:var(--muted);margin-bottom:8px;}
  .checkrow input{width:auto;}
  .legend-dot{width:8px;height:8px;border-radius:50%;display:inline-block;}
  .toolbar-note{font-size:10.5px;color:var(--warn);margin-top:6px;line-height:1.4;}
  ::-webkit-scrollbar{width:9px;height:9px;}
  ::-webkit-scrollbar-thumb{background:var(--line);border-radius:5px;}
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;h1&gt;Graph Digitizer&lt;/h1&gt;
&lt;p class="sub"&gt;Load a chart image, calibrate its axes against two known reference points on each, sample the curve's color, then extract a dense table of coordinate pairs. Everything runs locally in this page.&lt;/p&gt;

&lt;div class="layout"&gt;

  &lt;!-- LEFT: workflow --&gt;
  &lt;div&gt;
    &lt;div class="panel"&gt;
      &lt;div class="step" id="step-load" data-n="1"&gt;
        &lt;h3&gt;Load image&lt;/h3&gt;
        &lt;p class="hint"&gt;Drop a chart image, or browse for one.&lt;/p&gt;
        &lt;div class="drop" id="dropZone"&gt;Drop image here, or click to choose a file&lt;/div&gt;
        &lt;input type="file" id="fileInput" accept="image/*" style="display:none;"&gt;
      &lt;/div&gt;

      &lt;div class="step" id="step-x" data-n="2"&gt;
        &lt;h3&gt;Calibrate X axis&lt;/h3&gt;
        &lt;p class="hint"&gt;Click two points on the X axis whose values you know (e.g. two gridline ticks), entering the value after each click.&lt;/p&gt;
        &lt;div class="checkrow"&gt;&lt;input type="checkbox" id="xLog"&gt;&lt;label for="xLog" style="margin:0;"&gt;Log scale on X&lt;/label&gt;&lt;/div&gt;
        &lt;button id="btnCalibX" disabled&gt;Set X reference points&lt;/button&gt;
        &lt;div id="xStatus" class="series-meta" style="margin-top:6px;"&gt;&lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="step" id="step-y" data-n="3"&gt;
        &lt;h3&gt;Calibrate Y axis&lt;/h3&gt;
        &lt;p class="hint"&gt;Same idea, on the Y axis.&lt;/p&gt;
        &lt;div class="checkrow"&gt;&lt;input type="checkbox" id="yLog"&gt;&lt;label for="yLog" style="margin:0;"&gt;Log scale on Y&lt;/label&gt;&lt;/div&gt;
        &lt;button id="btnCalibY" disabled&gt;Set Y reference points&lt;/button&gt;
        &lt;div id="yStatus" class="series-meta" style="margin-top:6px;"&gt;&lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="step" id="step-series" data-n="4"&gt;
        &lt;h3&gt;Trace a curve&lt;/h3&gt;
        &lt;p class="hint"&gt;Name the series, then click directly on the curve in the image to sample its color. Adjust tolerance if it misses spots or catches noise.&lt;/p&gt;
        &lt;div class="row"&gt;
          &lt;div&gt;&lt;label&gt;Series name&lt;/label&gt;&lt;input type="text" id="seriesName" placeholder="e.g. Exxon curve"&gt;&lt;/div&gt;
        &lt;/div&gt;
        &lt;button id="btnPickColor" disabled&gt;Pick color from curve&lt;/button&gt;
        &lt;div id="colorPreview" class="series-meta" style="margin-top:6px;"&gt;&lt;/div&gt;
        &lt;div style="margin-top:10px;"&gt;
          &lt;label&gt;Color match tolerance: &lt;span id="tolVal"&gt;45&lt;/span&gt;&lt;/label&gt;
          &lt;input type="range" id="tolerance" min="2" max="160" value="45" style="width:100%;"&gt;
        &lt;/div&gt;
        &lt;div style="margin-top:10px;"&gt;
          &lt;button id="btnSetRegion" disabled style="width:100%;"&gt;Restrict to plot area (optional)&lt;/button&gt;
          &lt;div id="regionStatus" class="series-meta" style="margin-top:4px;"&gt;Not set — will scan the whole image. Set this if legend text, axis labels, or anything else shares the curve's color.&lt;/div&gt;
          &lt;button id="btnClearRegion" class="small" style="margin-top:6px;"&gt;Clear region (use whole image)&lt;/button&gt;
        &lt;/div&gt;
        &lt;button id="btnExtract" class="primary" disabled style="margin-top:10px;width:100%;"&gt;Extract curve&lt;/button&gt;
        &lt;p class="toolbar-note" id="extractNote"&gt;&lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="panel"&gt;
      &lt;h3 style="margin:0 0 8px;font-size:12.5px;"&gt;Series extracted&lt;/h3&gt;
      &lt;div id="seriesList"&gt;&lt;div class="empty"&gt;None yet&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;

  &lt;!-- CENTER: canvas --&gt;
  &lt;div class="canvas-wrap"&gt;
    &lt;div class="canvas-inner" id="canvasInner"&gt;
      &lt;canvas id="canvas" width="600" height="400"&gt;&lt;/canvas&gt;
      &lt;div class="readout" id="readout" style="display:none;"&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;

  &lt;!-- RIGHT: results --&gt;
  &lt;div class="panel"&gt;
    &lt;h3 style="margin:0 0 10px;font-size:12.5px;"&gt;Output&lt;/h3&gt;
    &lt;div class="row"&gt;
      &lt;div&gt;
        &lt;label&gt;X axis label&lt;/label&gt;
        &lt;input type="text" id="xLabel" value="X" placeholder="e.g. Millions of years ago"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="checkrow" style="margin-top:2px;"&gt;
      &lt;input type="checkbox" id="resampleOn"&gt;
      &lt;label for="resampleOn" style="margin:0;"&gt;Resample to even X steps&lt;/label&gt;
    &lt;/div&gt;
    &lt;div class="row" id="resampleRow" style="display:none;"&gt;
      &lt;div&gt;&lt;label&gt;Number of points&lt;/label&gt;&lt;input type="number" id="resampleN" value="200" min="2" max="5000"&gt;&lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="tabs"&gt;
      &lt;button id="tabTable" class="active"&gt;Table&lt;/button&gt;
      &lt;button id="tabCSV"&gt;CSV text&lt;/button&gt;
    &lt;/div&gt;
    &lt;div id="viewTable"&gt;&lt;div class="tablewrap"&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;x&lt;/th&gt;&lt;th id="thY"&gt;y&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody id="tableBody"&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;div id="viewCSV" style="display:none;"&gt;&lt;textarea id="csvText" readonly placeholder="Extract a curve to see data here."&gt;&lt;/textarea&gt;&lt;/div&gt;
    &lt;div class="flexgap"&gt;
      &lt;button id="btnCopy"&gt;Copy&lt;/button&gt;
      &lt;button id="btnDownload" class="primary"&gt;Download CSV&lt;/button&gt;
    &lt;/div&gt;
    &lt;label style="margin-top:12px;"&gt;Combine series into one file&lt;/label&gt;
    &lt;div class="flexgap" style="margin-top:0;"&gt;
      &lt;button id="btnDownloadAll"&gt;Download all (shared X grid)&lt;/button&gt;
    &lt;/div&gt;
  &lt;/div&gt;

&lt;/div&gt;

&lt;script&gt;
(() =&gt; {
  const $ = id =&gt; document.getElementById(id);
  const canvas = $('canvas'), ctx = canvas.getContext('2d', {willReadFrequently:true});
  const readout = $('readout');
  const canvasInner = $('canvasInner');

  let img = null, imgData = null;
  let calib = { x: [], y: [], xLog:false, yLog:false };
  let mode = null; // 'calibX' | 'calibY' | 'pickColor' | 'setRegion'
  let pendingCalibAxis = null;
  let series = []; // {name, color:, tolerance, points:}
  let activeSeries = null;
  let region = null; // {x0,x1,y0,y1} in pixel coords, or null = whole image
  let regionClickBuffer = [];

  // ---------- image loading ----------
  const dropZone = $('dropZone'), fileInput = $('fileInput');
  dropZone.addEventListener('click', () =&gt; fileInput.click());
  dropZone.addEventListener('dragover', e =&gt; { e.preventDefault(); dropZone.style.borderColor = 'var(--accent)'; });
  dropZone.addEventListener('dragleave', () =&gt; dropZone.style.borderColor = 'var(--line)');
  dropZone.addEventListener('drop', e =&gt; {
    e.preventDefault(); dropZone.style.borderColor = 'var(--line)';
    if (e.dataTransfer.files.length) loadFile(e.dataTransfer.files);
  });
  fileInput.addEventListener('change', e =&gt; { if (e.target.files.length) loadFile(e.target.files); });

  function loadFile(file) {
    const reader = new FileReader();
    reader.onload = ev =&gt; {
      const im = new Image();
      im.onload = () =&gt; {
        const MAXW = 1800;
        let w = im.naturalWidth, h = im.naturalHeight;
        if (w &gt; MAXW) { h = Math.round(h * MAXW / w); w = MAXW; }
        canvas.width = w; canvas.height = h;
        ctx.drawImage(im, 0, 0, w, h);
        img = im;
        imgData = ctx.getImageData(0, 0, w, h);
        dropZone.textContent = file.name + ' — loaded (' + w + '×' + h + 'px). Drop another to replace.';
        $('btnCalibX').disabled = false;
        $('btnCalibY').disabled = false;
        updateStepStates();
      };
      im.src = ev.target.result;
    };
    reader.readAsDataURL(file);
  }

  // ---------- coordinate helpers ----------
  function canvasCoords(evt) {
    const r = canvas.getBoundingClientRect();
    const scaleX = canvas.width / r.width, scaleY = canvas.height / r.height;
    return {
      px: (evt.clientX - r.left) * scaleX,
      py: (evt.clientY - r.top) * scaleY
    };
  }

  function mapAxis(pVal, p1, p2, log) {
    // linear/log interpolation given two calibration points {px, val}
    if (log) {
      const l1 = Math.log10(p1.val), l2 = Math.log10(p2.val);
      const l = l1 + (pVal - p1.px) * (l2 - l1) / (p2.px - p1.px);
      return Math.pow(10, l);
    }
    return p1.val + (pVal - p1.px) * (p2.val - p1.val) / (p2.px - p1.px);
  }

  function pixelToData(px, py) {
    if (calib.x.length &lt; 2 || calib.y.length &lt; 2) return null;
    const dataX = mapAxis(px, calib.x, calib.x, xLogOn());
    const dataY = mapAxis(py, calib.y, calib.y, yLogOn());
    return { dataX, dataY };
  }
  function xLogOn(){ return $('xLog').checked; }
  function yLogOn(){ return $('yLog').checked; }

  // ---------- mouse readout ----------
  canvas.addEventListener('mousemove', evt =&gt; {
    if (!img) return;
    const { px, py } = canvasCoords(evt);
    const r = canvas.getBoundingClientRect();
    readout.style.left = (evt.clientX - r.left) + 'px';
    readout.style.top = (evt.clientY - r.top) + 'px';
    readout.style.display = 'block';
    let text = 'px ' + Math.round(px) + ', ' + Math.round(py);
    if (calib.x.length === 2 &amp;&amp; calib.y.length === 2) {
      const d = pixelToData(px, py);
      if (d) text += '  ?  x=' + fmt(d.dataX) + ', y=' + fmt(d.dataY);
    }
    readout.textContent = text;
  });
  canvas.addEventListener('mouseleave', () =&gt; readout.style.display = 'none');

  function fmt(n){ return Math.abs(n) &gt;= 1000 ? n.toFixed(0) : n.toPrecision(4).replace(/\.?0+$/,''); }

  // ---------- calibration flow ----------
  $('btnCalibX').addEventListener('click', () =&gt; startCalib('x'));
  $('btnCalibY').addEventListener('click', () =&gt; startCalib('y'));

  function startCalib(axis) {
    calib = [];
    mode = 'calib' + axis.toUpperCase();
    pendingCalibAxis = axis;
    statusFor(axis).textContent = 'Click point 1 on the ' + axis.toUpperCase() + ' axis…';
    setButtonsBusy(true);
  }

  function statusFor(axis){ return axis === 'x' ? $('xStatus') : $('yStatus'); }

  canvas.addEventListener('click', evt =&gt; {
    if (!img) return;
    const { px, py } = canvasCoords(evt);

    if (mode === 'calibX' || mode === 'calibY') {
      const axis = pendingCalibAxis;
      askValue(axis === 'x' ? px : py, pt =&gt; {
        calib.push({ px: axis === 'x' ? px : py, val: pt });
        drawMarker(px, py, '#e8a33d');
        if (calib.length === 1) {
          statusFor(axis).textContent = 'Point 1 set. Click point 2 on the ' + axis.toUpperCase() + ' axis…';
        } else {
          statusFor(axis).textContent = axis.toUpperCase() + ' calibrated: ' +
            calib.val + ' ? ' + calib.val;
          mode = null; setButtonsBusy(false);
          updateStepStates();
        }
      });
      return;
    }

    if (mode === 'setRegion') {
      regionClickBuffer.push({ px, py });
      drawMarker(px, py, '#5fd3c4');
      if (regionClickBuffer.length === 1) {
        $('regionStatus').textContent = 'Top-left set. Click the bottom-right corner of the plot area…';
      } else {
        const  = regionClickBuffer;
        region = {
          x0: Math.min(a.px, b.px), x1: Math.max(a.px, b.px),
          y0: Math.min(a.py, b.py), y1: Math.max(a.py, b.py)
        };
        $('regionStatus').textContent = 'Region set: x ' + Math.round(region.x0) + '–' + Math.round(region.x1) +
          ', y ' + Math.round(region.y0) + '–' + Math.round(region.y1) + '. Extraction will ignore everything outside this box.';
        mode = null;
      }
      return;
    }

    if (mode === 'pickColor') {
      const idx = (Math.floor(py) * canvas.width + Math.floor(px)) * 4;
      const rC = imgData.data, g = imgData.data, b = imgData.data;
      activeSeries.color = ;
      $('colorPreview').innerHTML = '&lt;span class="swatch" style="background:rgb(' + rC + ',' + g + ',' + b + ')"&gt;&lt;/span&gt;sampled rgb(' + rC + ', ' + g + ', ' + b + ')';
      mode = null;
      $('btnExtract').disabled = false;
      redrawBase();
    }
  });

  // inline value-entry (no blocking dialogs — sandboxed iframes can block those)
  function askValue(pixelPos, onDone) {
    const panel = document.createElement('div');
    panel.style.cssText = 'margin-top:6px;display:flex;gap:6px;';
    panel.innerHTML = '&lt;input type="text" id="__valInput" placeholder="value at this point" style="flex:1;"&gt;&lt;button id="__valOk" class="small primary"&gt;Set&lt;/button&gt;';
    const host = mode === 'calibX' ? $('xStatus') : $('yStatus');
    host.after(panel);
    const input = panel.querySelector('#__valInput');
    input.focus();
    function commit() {
      const v = parseFloat(input.value);
      panel.remove();
      if (!isNaN(v)) onDone(v);
    }
    panel.querySelector('#__valOk').addEventListener('click', commit);
    input.addEventListener('keydown', e =&gt; { if (e.key === 'Enter') commit(); });
  }

  function setButtonsBusy(busy) {
    $('btnCalibX').disabled = busy;
    $('btnCalibY').disabled = busy;
  }

  function drawMarker(px, py, color) {
    ctx.save();
    ctx.strokeStyle = color; ctx.lineWidth = 2;
    ctx.beginPath(); ctx.arc(px, py, 5, 0, Math.PI*2); ctx.stroke();
    ctx.restore();
  }

  function redrawBase() {
    if (!img) return;
    ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
  }

  // ---------- series ----------
  $('btnPickColor').addEventListener('click', () =&gt; {
    const name = $('seriesName').value.trim() || ('Series ' + (series.length + 1));
    activeSeries = { name, color: null, tolerance: parseInt($('tolerance').value,10), points: [] };
    mode = 'pickColor';
    $('colorPreview').textContent = 'Click on the curve in the image…';
  });

  $('btnSetRegion').addEventListener('click', () =&gt; {
    regionClickBuffer = [];
    mode = 'setRegion';
    $('regionStatus').textContent = 'Click the top-left corner of the plot area…';
  });

  $('btnClearRegion').addEventListener('click', () =&gt; {
    region = null;
    regionClickBuffer = [];
    mode = null;
    $('regionStatus').textContent = 'Not set — will scan the whole image. Set this if legend text, axis labels, or anything else shares the curve\'s color.';
  });

  $('tolerance').addEventListener('input', e =&gt; { $('tolVal').textContent = e.target.value; if (activeSeries) activeSeries.tolerance = parseInt(e.target.value,10); });

  $('btnExtract').addEventListener('click', () =&gt; {
    if (!activeSeries || !activeSeries.color || calib.x.length &lt; 2 || calib.y.length &lt; 2) return;
    const  = activeSeries.color;
    const baseTol = activeSeries.tolerance;
    const w = canvas.width, h = canvas.height;
    const data = imgData.data;
    // hard-clamp to the pixel range you calibrated — those ARE the graph's real limits,
    // so we never scan (or report data from) outside them. A region box can only narrow this further.
    const calibXMin = Math.min(calib.x.px, calib.x.px), calibXMax = Math.max(calib.x.px, calib.x.px);
    const calibYMin = Math.min(calib.y.px, calib.y.px), calibYMax = Math.max(calib.y.px, calib.y.px);
    let xStart = Math.round(calibXMin), xEnd = Math.round(calibXMax);
    let yStart = Math.round(calibYMin), yEnd = Math.round(calibYMax);
    if (region) {
      xStart = Math.max(xStart, Math.floor(region.x0)); xEnd = Math.min(xEnd, Math.ceil(region.x1));
      yStart = Math.max(yStart, Math.floor(region.y0)); yEnd = Math.min(yEnd, Math.ceil(region.y1));
    }
    xStart = Math.max(0, xStart); xEnd = Math.min(w - 1, xEnd);
    yStart = Math.max(0, yStart); yEnd = Math.min(h - 1, yEnd);

    function matchesAt(x, tol) {
      const out = [];
      for (let y = yStart; y &lt;= yEnd; y++) {
        const idx = (y * w + x) * 4;
        const dr = data-cr, dg = data-cg, db = data-cb;
        if (Math.sqrt(dr*dr+dg*dg+db*db) &lt;= tol) out.push(y);
      }
      return out;
    }

    function runsOf(matches) {
      const runs = [];
      if (!matches.length) return runs;
      let runStart = matches, prev = matches;
      for (let i = 1; i &lt;= matches.length; i++) {
        const y = matches;
        if (y === undefined || y - prev &gt; 2) {
          runs.push({ centroid: (runStart + prev) / 2, size: prev - runStart + 1 });
          if (y !== undefined) runStart = y;
        }
        prev = y;
      }
      return runs;
    }

    const pts = [];
    let lastPy = null;
    let stretchedColumns = 0, deadColumns = [];
    const TOL_STEPS = ; // multipliers tried in order until a match is found

    for (let x = xStart; x &lt;= xEnd; x++) {
      let matches = [], usedTol = baseTol;
      for (const mult of TOL_STEPS) {
        usedTol = baseTol * mult;
        matches = matchesAt(x, usedTol);
        if (matches.length) { if (mult &gt; 1) stretchedColumns++; break; }
      }
      if (!matches.length) { deadColumns.push(x); continue; }

      const runs = runsOf(matches);
      let chosen;
      if (lastPy === null) {
        chosen = runs.reduce((a, b) =&gt; (b.size &gt; a.size ? b : a), runs);
      } else {
        chosen = runs.reduce((a, b) =&gt;
          Math.abs(b.centroid - lastPy) &lt; Math.abs(a.centroid - lastPy) ? b : a, runs);
      }
      lastPy = chosen.centroid;
      const d = pixelToData(x, chosen.centroid);
      pts.push({ px: x, dataX: d.dataX, dataY: d.dataY });
    }
    activeSeries.points = pts.sort((a,b) =&gt; a.dataX - b.dataX);
    series.push(activeSeries);

    // auto-fill short gaps (a handful of pixels — typically where another curve/gridline
    // crosses this one and briefly steals the match) by interpolating; leave long gaps alone
    const FILL_MAX_PX = 8;
    const ptsByPx = activeSeries.points.slice().sort((a,b) =&gt; a.px - b.px);
    const filled = [];
    let filledGapCount = 0;
    for (let i = 0; i &lt; ptsByPx.length; i++) {
      filled.push(ptsByPx);
      if (i &lt; ptsByPx.length - 1) {
        const gap = ptsByPx.px - ptsByPx.px;
        if (gap &gt; 1 &amp;&amp; gap &lt;= FILL_MAX_PX + 1) {
          filledGapCount++;
          for (let px = ptsByPx.px + 1; px &lt; ptsByPx.px; px++) {
            const t = (px - ptsByPx.px) / gap;
            filled.push({
              px,
              dataX: ptsByPx.dataX + t * (ptsByPx.dataX - ptsByPx.dataX),
              dataY: ptsByPx.dataY + t * (ptsByPx.dataY - ptsByPx.dataY),
              interpolated: true
            });
          }
        }
      }
    }
    activeSeries.points = filled.sort((a,b) =&gt; a.dataX - b.dataX);

    // report exactly what was and wasn't covered, in data units, so gaps are diagnosable
    const scannedCols = xEnd - xStart + 1;
    const fullRangeD = pixelToData(xStart, yStart), fullRangeD2 = pixelToData(xEnd, yStart);
    let note = pts.length + ' of ' + scannedCols + ' scanned columns matched directly (' + stretchedColumns + ' needed relaxed tolerance), plus ' +
      filledGapCount + ' short gap(s) auto-filled by interpolation (likely line crossings).';
    if (pts.length) {
      const xs = pts.map(p =&gt; p.dataX);
      note += ' Data spans x=' + fmt(Math.min(...xs)) + ' to x=' + fmt(Math.max(...xs)) +
        '. Scan was clamped to your calibrated range, x=' + fmt(Math.min(fullRangeD.dataX, fullRangeD2.dataX)) + ' to x=' + fmt(Math.max(fullRangeD.dataX, fullRangeD2.dataX)) + '.';
    }
    // only report gaps too wide to have been auto-filled above
    const longDeadColumns = [];
    {
      let gs = null, gp = null;
      const flushIfLong = () =&gt; { if (gs !== null &amp;&amp; (gp - gs) &gt; FILL_MAX_PX) longDeadColumns.push(); };
      for (const c of deadColumns) {
        if (gs === null) { gs = c; gp = c; }
        else if (c - gp &lt;= 1) { gp = c; }
        else { flushIfLong(); gs = c; gp = c; }
      }
      flushIfLong();
    }
    if (longDeadColumns.length) {
      const gapText = longDeadColumns.slice(0, 6).map(() =&gt; {
        const da = pixelToData(a, yStart).dataX, db = pixelToData(b, yStart).dataX;
        return fmt(Math.min(da,db)) + '–' + fmt(Math.max(da,db));
      }).join(', ');
      note += ' No match (even relaxed) over a stretch wider than a crossing, in: ' + gapText +
        (longDeadColumns.length &gt; 6 ? ', +' + (longDeadColumns.length-6) + ' more' : '') +
        ' — likely the line is genuinely a different color there, or absent from the source.';
    }
    $('extractNote').textContent = note;
    renderSeriesList();
    renderOutput();
    activeSeries = null;
    $('btnExtract').disabled = true;
    $('seriesName').value = '';
    $('colorPreview').textContent = '';
  });

  function renderSeriesList() {
    const host = $('seriesList');
    if (!series.length) { host.innerHTML = '&lt;div class="empty"&gt;None yet&lt;/div&gt;'; return; }
    host.innerHTML = '';
    series.forEach((s, i) =&gt; {
      const div = document.createElement('div');
      div.className = 'series-item';
      div.innerHTML = '&lt;span class="swatch" style="background:rgb(' + s.color.join(',') + ')"&gt;&lt;/span&gt;' +
        '&lt;span class="series-name"&gt;' + s.name + '&lt;/span&gt;' +
        '&lt;div class="series-meta"&gt;' + s.points.length + ' points · tolerance ' + s.tolerance + '&lt;/div&gt;' +
        '&lt;div class="flexgap"&gt;&lt;button class="small" data-act="view"&gt;View&lt;/button&gt;&lt;button class="small" data-act="remove"&gt;Remove&lt;/button&gt;&lt;/div&gt;';
      div.querySelector('').addEventListener('click', () =&gt; { renderOutput(i); });
      div.querySelector('').addEventListener('click', () =&gt; { series.splice(i,1); renderSeriesList(); renderOutput(); });
      host.appendChild(div);
    });
  }

  // ---------- output ----------
  $('tabTable').addEventListener('click', () =&gt; switchTab('table'));
  $('tabCSV').addEventListener('click', () =&gt; switchTab('csv'));
  function switchTab(which) {
    $('tabTable').classList.toggle('active', which==='table');
    $('tabCSV').classList.toggle('active', which==='csv');
    $('viewTable').style.display = which==='table' ? '' : 'none';
    $('viewCSV').style.display = which==='csv' ? '' : 'none';
  }
  $('resampleOn').addEventListener('change', e =&gt; { $('resampleRow').style.display = e.target.checked ? '' : 'none'; renderOutput(currentViewIdx); });
  $('resampleN').addEventListener('change', () =&gt; renderOutput(currentViewIdx));
  $('xLabel').addEventListener('input', () =&gt; renderOutput(currentViewIdx));

  let currentViewIdx = 0;
  function renderOutput(idx) {
    if (idx !== undefined) currentViewIdx = idx;
    if (!series.length) {
      $('tableBody').innerHTML = '';
      $('csvText').value = '';
      $('thY').textContent = 'y';
      return;
    }
    const s = series;
    $('thY').textContent = s.name;
    let pts = s.points;
    if ($('resampleOn').checked) pts = resample(pts, parseInt($('resampleN').value,10) || 200);

    const xLabel = $('xLabel').value || 'x';
    let csv = xLabel + ',' + s.name + '\n';
    let rows = '';
    pts.forEach(p =&gt; {
      csv += p.dataX.toPrecision(6) + ',' + p.dataY.toPrecision(6) + '\n';
      rows += '&lt;tr&gt;&lt;td&gt;' + fmt(p.dataX) + '&lt;/td&gt;&lt;td&gt;' + fmt(p.dataY) + '&lt;/td&gt;&lt;/tr&gt;';
    });
    $('tableBody').innerHTML = rows;
    $('csvText').value = csv;
  }

  function resample(pts, n) {
    if (pts.length &lt; 2) return pts;
    const xs = pts.map(p =&gt; p.dataX);
    const xMin = Math.min(...xs), xMax = Math.max(...xs);
    const out = [];
    for (let i = 0; i &lt; n; i++) {
      const xt = xMin + (xMax - xMin) * i / (n - 1);
      out.push({ dataX: xt, dataY: interpAt(pts, xt) });
    }
    return out;
  }
  function interpAt(pts, xt) {
    for (let i = 0; i &lt; pts.length - 1; i++) {
      const a = pts, b = pts;
      if ((xt &gt;= a.dataX &amp;&amp; xt &lt;= b.dataX) || (xt &lt;= a.dataX &amp;&amp; xt &gt;= b.dataX)) {
        const t = (xt - a.dataX) / (b.dataX - a.dataX || 1);
        return a.dataY + t * (b.dataY - a.dataY);
      }
    }
    return pts.dataY;
  }

  $('btnCopy').addEventListener('click', () =&gt; {
    $('csvText').select();
    navigator.clipboard.writeText($('csvText').value).catch(()=&gt;{ document.execCommand('copy'); });
  });
  $('btnDownload').addEventListener('click', () =&gt; {
    if (!series.length) return;
    const s = series;
    downloadText($('csvText').value, (s.name || 'series').replace(/\s+/g,'_') + '.csv');
  });
  $('btnDownloadAll').addEventListener('click', () =&gt; {
    if (!series.length) return;
    const useResample = $('resampleOn').checked;
    const n = parseInt($('resampleN').value,10) || 200;
    const allX = series.flatMap(s =&gt; s.points.map(p =&gt; p.dataX));
    const xMin = Math.min(...allX), xMax = Math.max(...allX);
    const count = useResample ? n : 200;
    const xLabel = $('xLabel').value || 'x';
    let header = xLabel + ',' + series.map(s =&gt; s.name).join(',');
    let csv = header + '\n';
    for (let i = 0; i &lt; count; i++) {
      const xt = xMin + (xMax - xMin) * i / (count - 1);
      const row = ;
      series.forEach(s =&gt; row.push(interpAt(s.points, xt).toPrecision(6)));
      csv += row.join(',') + '\n';
    }
    downloadText(csv, 'combined_series.csv');
  });

  function downloadText(text, filename) {
    const blob = new Blob(, {type:'text/csv'});
    const a = document.createElement('a');
    a.href = URL.createObjectURL(blob);
    a.download = filename;
    a.click();
    URL.revokeObjectURL(a.href);
  }

  function updateStepStates() {
    $('step-load').classList.toggle('done', !!img);
    $('step-x').classList.toggle('active', !!img &amp;&amp; calib.x.length &lt; 2);
    $('step-x').classList.toggle('done', calib.x.length === 2);
    $('step-y').classList.toggle('active', calib.x.length === 2 &amp;&amp; calib.y.length &lt; 2);
    $('step-y').classList.toggle('done', calib.y.length === 2);
    $('step-series').classList.toggle('active', calib.x.length === 2 &amp;&amp; calib.y.length === 2);
    $('btnPickColor').disabled = !(calib.x.length === 2 &amp;&amp; calib.y.length === 2);
    $('btnSetRegion').disabled = !(calib.x.length === 2 &amp;&amp; calib.y.length === 2);
  }
})();
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>]]></content:encoded>
						                            <category domain="https://cyclesresearchinstitute.org/community/"></category>                        <dc:creator>RayTomes</dc:creator>
                        <guid isPermaLink="true">https://cyclesresearchinstitute.org/community/methods/graph-digitizer/</guid>
                    </item>
				                    <item>
                        <title>Kotov&#039;s Method App</title>
                        <link>https://cyclesresearchinstitute.org/community/methods/kotovs-method-app/</link>
                        <pubDate>Sun, 30 Aug 2026 07:26:06 +0000</pubDate>
                        <description><![CDATA[The following is an app for calculating Kotov&#039; Method for communality of a set of values. You just load it into a browser and then past or type in the list of values to the window, select th...]]></description>
                        <content:encoded><![CDATA[<p>The following is an app for calculating Kotov' Method for communality of a set of values. You just load it into a browser and then past or type in the list of values to the window, select the range over which you want the spectrum and press compute spectrum. It is preloaded with the Planets distance from the Sun for you to see the sort of results that you can expect.</p>
<p>In my research this is a method that I have used very extensively with great results. When I ran this on the radius of black holes recently (there is much better data now than when I wrote on it in my book)  it showed that exactly the same two big peaks occurred in black holes as in the solar system. Quite clearly the same waves pervade the entire Universe (as black holes are at the centre of most or all galaxies).</p>
<p>I cannot recommend this technique too highly. Thank you Kotov who sadly died late last year. He also discovered the 160 minute cycle in the Sun, which wikipedia erroneously says is not real. He showed that the 160 minute cycle is present in many classes of objects - binaries, asteroid rotations, etc as well as the planets.</p>
<pre contenteditable="false">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
&lt;meta charset="UTF-8"&gt;
&lt;title&gt;Kotov's Method v2 — Black Hole Event Horizon Radii&lt;/title&gt;
&lt;style&gt;
  body{background:#fff; color:#1c1c18; margin:0; padding:24px;}
  .kwc-wrap{--paper:transparent; --ink:#1c1c18; --rule:#ddd; --accent:#2f6f66; --accent-soft:rgba(47,111,102,0.12); --muted:#666;
    font-family: Georgia, 'Iowan Old Style', 'Palatino Linotype', serif; color:var(--ink); max-width:1100px; margin:0 auto;}
  .kwc-wrap .mono{font-family:'IBM Plex Mono','SFMono-Regular',Menlo,Consolas,monospace;}
  .kwc-wrap h1{margin:0 0 4px 0; font-size:1.3rem; font-weight:600;}
  .kwc-wrap &gt; p{margin:0 0 16px 0; color:var(--muted); font-size:.88rem; max-width:70ch;}
  .kwc-layout{display:grid; grid-template-columns: 300px 1fr; gap:20px;}
  @media (max-width:820px){ .kwc-layout{grid-template-columns:1fr;} }
  .kwc-panel{padding-right:16px; border-right:1px solid var(--rule);}
  @media (max-width:820px){ .kwc-panel{border-right:none; border-bottom:1px solid var(--rule); padding-bottom:16px;} }
  .kwc-field{margin-bottom:14px;}
  .kwc-field label{display:block; font-size:.72rem; text-transform:uppercase; letter-spacing:.06em; color:var(--muted); margin-bottom:5px;}
  .kwc-wrap textarea, .kwc-wrap input, .kwc-wrap input{
    width:100%; font-family:'IBM Plex Mono','SFMono-Regular',Menlo,Consolas,monospace;
    font-size:.78rem; padding:6px 8px; border:1px solid var(--rule); border-radius:3px;
    background:#fafafa; color:var(--ink); box-sizing:border-box;
  }
  .kwc-wrap textarea{height:280px; resize:vertical; line-height:1.4;}
  .kwc-row{display:flex; gap:8px;}
  .kwc-row .kwc-field{flex:1;}
  .kwc-wrap fieldset{border:1px solid var(--rule); border-radius:4px; padding:8px 10px; margin:0 0 14px 0;}
  .kwc-wrap legend{font-size:.72rem; text-transform:uppercase; letter-spacing:.06em; color:var(--muted); padding:0 5px;}
  .kwc-radiorow{display:flex; gap:12px; align-items:center; font-size:.82rem;}
  .kwc-radiorow label{display:flex; align-items:center; gap:5px; color:var(--ink); text-transform:none; letter-spacing:0;}
  .kwc-wrap button{font-family:inherit; font-size:.85rem; padding:8px 14px; border-radius:3px; border:1px solid var(--ink); cursor:pointer; background:#fff;}
  .kwc-wrap button:hover{background:#f0f0f0;}
  .kwc-btnrow{display:flex; gap:8px; flex-wrap:wrap; margin-top:4px;}
  .kwc-plotwrap{border:1px solid var(--rule); padding:16px; border-radius:4px;}
  .kwc-plottitle{font-size:.88rem; margin:0 0 10px 0;}
  #kwc-plot{width:100%; height:300px; display:block;}
  .kwc-axislabel{text-align:center; font-size:.72rem; color:var(--muted); margin-top:2px;}
  .kwc-wrap table{width:100%; border-collapse:collapse; font-size:.82rem;}
  .kwc-wrap th,.kwc-wrap td{text-align:left; padding:5px 8px; border-bottom:1px solid var(--rule);}
  .kwc-wrap th{color:var(--muted); font-weight:600; text-transform:uppercase; font-size:.68rem;}
  .kwc-score-bar{display:inline-block; height:6px; background:var(--accent); border-radius:2px; vertical-align:middle; margin-right:6px;}
  .kwc-peaks{margin-top:18px;}
  .kwc-peaks h2{font-size:.88rem; margin:0 0 8px 0;}
  .kwc-note{font-size:.72rem; color:var(--muted); margin-top:8px; line-height:1.5;}
  .kwc-peakrow{cursor:pointer;}
  .kwc-peakrow:hover{background:#f5f5f5;}
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;div class="kwc-wrap"&gt;
&lt;h1&gt;Kotov's Method v2&lt;/h1&gt;
&lt;p&gt;Click any row in the peak table below to highlight that point on the plot and see the per-point fit breakdown.&lt;/p&gt;

&lt;div class="kwc-layout"&gt;
  &lt;div class="kwc-panel"&gt;
    &lt;div class="kwc-field"&gt;
      &lt;label for="kwc-values"&gt;Input values — one per line: value, optional ", weight"&lt;/label&gt;
      &lt;textarea id="kwc-values" class="mono"&gt;0.387, Mercury
0.723, Venus
1.000, Earth
1.524, Mars
5.203, Jupiter
9.537, Saturn
19.191, Uranus
30.069, Neptune
39.482, Pluto (dwarf planet)&lt;/textarea&gt;
    &lt;/div&gt;
    &lt;div class="kwc-row"&gt;
      &lt;div class="kwc-field"&gt;&lt;label for="kwc-qmin"&gt;Range min&lt;/label&gt;&lt;input type="number" id="kwc-qmin" value="0.1" step="any"&gt;&lt;/div&gt;
      &lt;div class="kwc-field"&gt;&lt;label for="kwc-qmax"&gt;Range max&lt;/label&gt;&lt;input type="number" id="kwc-qmax" value="150" step="any"&gt;&lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="kwc-field"&gt;
      &lt;label for="kwc-stepsize"&gt;Coarse step size (% per step, log scale)&lt;/label&gt;
      &lt;input type="number" id="kwc-stepsize" value="0.1" step="any"&gt;
      &lt;div id="kwc-stepcount" class="mono" style="font-size:.68rem; color:var(--muted); margin-top:4px;"&gt;&lt;/div&gt;
    &lt;/div&gt;
    &lt;fieldset&gt;
      &lt;legend&gt;Error exponent&lt;/legend&gt;
      &lt;div class="kwc-radiorow"&gt;
        &lt;label&gt;&lt;input type="radio" name="kwc-power" value="1" checked&gt; 1&lt;/label&gt;
        &lt;label&gt;&lt;input type="radio" name="kwc-power" value="2"&gt; 2&lt;/label&gt;
        &lt;label&gt;&lt;input type="radio" name="kwc-power" value="3"&gt; 3&lt;/label&gt;
      &lt;/div&gt;
    &lt;/fieldset&gt;
    &lt;div class="kwc-btnrow"&gt;
      &lt;button id="kwc-run"&gt;Compute spectrum&lt;/button&gt;
    &lt;/div&gt;
  &lt;/div&gt;

  &lt;div&gt;
    &lt;div class="kwc-plotwrap"&gt;
      &lt;p class="kwc-plottitle" id="kwc-plottitle"&gt;Commensurability spectrum&lt;/p&gt;
      &lt;canvas id="kwc-plot"&gt;&lt;/canvas&gt;
      &lt;p class="kwc-axislabel"&gt;Trial quantum value (log scale)&lt;/p&gt;
    &lt;/div&gt;
    &lt;div class="kwc-peaks"&gt;
      &lt;h2&gt;Strongest peaks (refined) — click a row to highlight on the plot&lt;/h2&gt;
      &lt;div class="kwc-radiorow" style="margin-bottom:8px;"&gt;
        &lt;label&gt;&lt;input type="radio" name="kwc-sortmode" value="strength"&gt; Strongest first&lt;/label&gt;
        &lt;label&gt;&lt;input type="radio" name="kwc-sortmode" value="numeric" checked&gt; Numerical order&lt;/label&gt;
      &lt;/div&gt;
      &lt;table&gt;
        &lt;thead&gt;&lt;tr&gt;&lt;th&gt;#&lt;/th&gt;&lt;th&gt;Trial value&lt;/th&gt;&lt;th&gt;Relative strength&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;
        &lt;tbody id="kwc-peaktable"&gt;&lt;/tbody&gt;
      &lt;/table&gt;
    &lt;/div&gt;
    &lt;div class="kwc-peaks" id="kwc-breakdown-section" style="display:none;"&gt;
      &lt;h2 id="kwc-breakdown-title"&gt;Fit breakdown&lt;/h2&gt;
      &lt;table&gt;
        &lt;thead&gt;&lt;tr&gt;&lt;th&gt;Input value&lt;/th&gt;&lt;th&gt;Ratio&lt;/th&gt;&lt;th&gt;Nearest integer&lt;/th&gt;&lt;th&gt;Deviation&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;
        &lt;tbody id="kwc-breakdowntable"&gt;&lt;/tbody&gt;
      &lt;/table&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;script&gt;
(function(){
let lastPlotQs=[], lastPlotScores=[], lastPeaks=[], highlightQ=null, lastVals=[], lastBaseline=null;

function parseValues(){
  const lines = document.getElementById('kwc-values').value.split(/\n/).map(s=&gt;s.trim()).filter(s=&gt;s.length&gt;0);
  const pairs = [];
  for(const line of lines){
    const parts = line.split(',').map(s=&gt;s.trim());
    const v = parseFloat(parts);
    if(isNaN(v)) continue;
    const label = parts.length&gt;1 ? parts.slice(1).join(', ') : null;
    pairs.push({v,w:1,label});
  }
  return pairs;
}

function scoreAt(q, vals, power){
  let err=0;
  for(const {v,w} of vals){
    const larger = q&gt;=v;
    const r = larger? q/v : v/q;
    const delta = Math.abs(r-Math.round(r));
    err += w*Math.pow(delta,power);
  }
  return 1/Math.max(err,1e-9);
}

function computeRandomBaseline(vals, qmin, qmax, power, nTrials){
  const logMin=Math.log(qmin), logMax=Math.log(qmax);
  const scores=[];
  for(let i=0;i&lt;nTrials;i++){
    const q = Math.exp(logMin + Math.random()*(logMax-logMin));
    scores.push(scoreAt(q, vals, power));
  }
  const mean = scores.reduce((a,b)=&gt;a+b,0)/scores.length;
  const variance = scores.reduce((a,b)=&gt;a+(b-mean)*(b-mean),0)/scores.length;
  const sd = Math.sqrt(variance);
  return {mean, sd};
}

function drawPlot(qs, scores, baseline){
  const canvas = document.getElementById('kwc-plot');
  const rect = canvas.getBoundingClientRect();
  const dpr = window.devicePixelRatio||1;
  canvas.width = rect.width*dpr; canvas.height = rect.height*dpr;
  const ctx = canvas.getContext('2d');
  ctx.scale(dpr,dpr);
  const W=rect.width, H=rect.height;
  const padL=48, padR=10, padT=8, padB=22;
  const plotW=W-padL-padR, plotH=H-padT-padB;
  const logMin=Math.log10(qs), logMax=Math.log10(qs);
  const twoSD = baseline ? baseline.mean+2*baseline.sd : 0;
  const lowest=Math.min(...scores), highest=Math.max(Math.max(...scores), twoSD);
  const span=highest-lowest;
  const yMax=highest+span*0.08, yMin=Math.max(0, lowest-span*0.1);
  function xPix(q){ return padL + (Math.log10(q)-logMin)/(logMax-logMin)*plotW; }
  function yPix(s){ return padT + plotH - ((s-yMin)/(yMax-yMin))*plotH; }

  ctx.strokeStyle = 'rgba(128,128,128,0.25)';
  ctx.fillStyle = '#888';
  ctx.font='10px monospace'; ctx.textAlign='center';
  let decade=Math.floor(logMin);
  while(decade&lt;=Math.ceil(logMax)){
    for(const m of ){
      const val=m*Math.pow(10,decade);
      if(val&gt;=qs &amp;&amp; val&lt;=qs){
        const x=xPix(val);
        ctx.beginPath(); ctx.moveTo(x,padT); ctx.lineTo(x,padT+plotH); ctx.stroke();
        ctx.fillText(val&gt;=1?val.toFixed(0):val.toString(), x, padT+plotH+14);
      }
    }
    decade++;
  }

  if(baseline){
    ctx.textAlign='left'; ctx.font='9px monospace';
    ctx.strokeStyle = '#888'; ctx.lineWidth=1; ctx.setLineDash();
    let y = yPix(baseline.mean);
    ctx.beginPath(); ctx.moveTo(padL,y); ctx.lineTo(padL+plotW,y); ctx.stroke();
    ctx.fillStyle='#888'; ctx.fillText('expected', padL+plotW-50, y-3);

    ctx.strokeStyle = '#b08a2f';
    y = yPix(twoSD);
    ctx.beginPath(); ctx.moveTo(padL,y); ctx.lineTo(padL+plotW,y); ctx.stroke();
    ctx.fillStyle='#b08a2f'; ctx.fillText('2 s.d.', padL+plotW-40, y-3);
    ctx.setLineDash([]);
  }

  ctx.strokeStyle = '#1c1c18';
  ctx.lineWidth=1; ctx.beginPath();
  for(let i=0;i&lt;qs.length;i++){
    const x=xPix(qs), y=yPix(scores);
    if(i===0) ctx.moveTo(x,y); else ctx.lineTo(x,y);
  }
  ctx.stroke();
  ctx.strokeRect(padL,padT,plotW,plotH);

  if(highlightQ!==null &amp;&amp; highlightQ&gt;=qs &amp;&amp; highlightQ&lt;=qs){
    const x = xPix(highlightQ);
    ctx.strokeStyle = '#c0392b';
    ctx.lineWidth = 1.5;
    ctx.setLineDash();
    ctx.beginPath(); ctx.moveTo(x,padT); ctx.lineTo(x,padT+plotH); ctx.stroke();
    ctx.setLineDash([]);
  }
}

function renderPeakTable(peaks){
  lastPeaks=peaks;
  const sortMode = document.querySelector('input:checked').value;
  const ordered = peaks.slice().sort((a,b)=&gt; sortMode==='numeric'? a.q-b.q : b.score-a.score);
  const tbody=document.getElementById('kwc-peaktable');
  tbody.innerHTML='';
  const maxScore = peaks.length? Math.max(...peaks.map(p=&gt;p.score)):1;
  ordered.forEach((p,idx)=&gt;{
    const tr=document.createElement('tr');
    tr.className='kwc-peakrow';
    const barW=Math.round((p.score/maxScore)*70);
    tr.innerHTML = `&lt;td&gt;${idx+1}&lt;/td&gt;&lt;td class="mono"&gt;${p.q.toPrecision(6)}&lt;/td&gt;
      &lt;td&gt;&lt;span class="kwc-score-bar" style="width:${barW}px"&gt;&lt;/span&gt;&lt;span class="mono"&gt;${p.score.toFixed(3)}&lt;/span&gt;&lt;/td&gt;`;
    tr.addEventListener('click', ()=&gt;{
      highlightQ=p.q; drawPlot(lastPlotQs,lastPlotScores,lastBaseline);
      showBreakdown(p.q);
    });
    tbody.appendChild(tr);
  });
}

document.querySelectorAll('input').forEach(el=&gt;{
  el.addEventListener('change', ()=&gt;{ if(lastPeaks.length) renderPeakTable(lastPeaks); });
});

function showBreakdown(q){
  const tbody = document.getElementById('kwc-breakdowntable');
  tbody.innerHTML='';
  const rows = lastVals.map((item,i)=&gt;{
    const larger = q&gt;=item.v;
    const r = larger? q/item.v : item.v/q;
    const nearest = Math.round(r);
    const dev = Math.abs(r-nearest);
    return {label: item.label || `${item.v}`, ratio:r, nearest, dev};
  });
  rows.sort((a,b)=&gt;a.dev-b.dev);
  for(const row of rows){
    const tr=document.createElement('tr');
    tr.innerHTML = `&lt;td&gt;${row.label}&lt;/td&gt;&lt;td class="mono"&gt;${row.ratio.toFixed(5)}&lt;/td&gt;&lt;td class="mono"&gt;${row.nearest}&lt;/td&gt;&lt;td class="mono"&gt;${row.dev.toFixed(5)}&lt;/td&gt;`;
    tbody.appendChild(tr);
  }
  document.getElementById('kwc-breakdown-title').textContent = `Fit breakdown for trial value ${q.toPrecision(6)}`;
  document.getElementById('kwc-breakdown-section').style.display='';
}

function run(){
  const vals = parseValues();
  if(vals.length===0){ return; }
  lastVals = vals;
  highlightQ=null;
  document.getElementById('kwc-breakdown-section').style.display='none';
  const qmin = parseFloat(document.getElementById('kwc-qmin').value);
  const qmax = parseFloat(document.getElementById('kwc-qmax').value);
  const stepPct = Math.max(0.001, parseFloat(document.getElementById('kwc-stepsize').value)||0.1);
  const power = parseFloat(document.querySelector('input:checked').value);
  const stepRatio = 1+stepPct/100;
  let n = Math.ceil(Math.log(qmax/qmin)/Math.log(stepRatio))+1;
  const capped = n&gt;200000;
  if(capped) n=200000;
  document.getElementById('kwc-stepcount').textContent = n.toLocaleString()+' coarse trial values'+(capped?' (capped)':'');

  const logMin=Math.log(qmin), logMax=Math.log(qmax);
  const coarseQs=[], coarseScores=[];
  for(let i=0;i&lt;n;i++){
    const q = Math.exp(logMin + (logMax-logMin)*i/(n-1));
    coarseQs.push(q); coarseScores.push(scoreAt(q, vals, power));
  }

  const candIdx=[];
  for(let i=1;i&lt;coarseScores.length-1;i++){
    if(coarseScores&gt;coarseScores &amp;&amp; coarseScores&gt;=coarseScores) candIdx.push(i);
  }
  candIdx.sort((a,b)=&gt;coarseScores-coarseScores);
  const picked=[];
  for(const i of candIdx){
    const q=coarseQs;
    if(picked.every(p=&gt; q &gt; p*(1+1/100) || q &lt; p/(1+1/100))) picked.push(q);
    if(picked.length&gt;=15) break;
  }

  const finePoints = [];
  const refinedPeaks = [];
  for(const q0 of picked){
    let best={q:q0, score:scoreAt(q0,vals,power)};
    const windowFactor = 1 + stepPct/100 * 3;
    const nFine = 400;
    const flogMin = Math.log(q0/windowFactor), flogMax = Math.log(q0*windowFactor);
    for(let j=0;j&lt;nFine;j++){
      const q = Math.exp(flogMin + (flogMax-flogMin)*j/(nFine-1));
      const s = scoreAt(q, vals, power);
      finePoints.push();
      if(s&gt;best.score) best={q,score:s};
    }
    refinedPeaks.push(best);
  }

  const merged = coarseQs.map((q,i)=&gt;[q,coarseScores]).concat(finePoints);
  merged.sort((a,b)=&gt;a-b);
  const plotQs = merged.map(p=&gt;p);
  const plotScores = merged.map(p=&gt;p);
  lastPlotQs=plotQs; lastPlotScores=plotScores;

  const uniqPeaks=[];
  refinedPeaks.sort((a,b)=&gt;b.score-a.score);
  for(const p of refinedPeaks){ if(uniqPeaks.every(u=&gt;p.q&gt;u.q*1.01||p.q&lt;u.q/1.01)) uniqPeaks.push(p); }

  lastBaseline = computeRandomBaseline(vals, qmin, qmax, power, 5000);

  drawPlot(plotQs, plotScores, lastBaseline);
  renderPeakTable(uniqPeaks);
  document.getElementById('kwc-plottitle').textContent = `Commensurability spectrum (adaptively refined) — p=${power}, range `;
}

document.getElementById('kwc-run').addEventListener('click', run);
window.addEventListener('resize', ()=&gt;{ if(lastPlotQs.length) drawPlot(lastPlotQs,lastPlotScores,lastBaseline); });
run();
})();
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>]]></content:encoded>
						                            <category domain="https://cyclesresearchinstitute.org/community/"></category>                        <dc:creator>RayTomes</dc:creator>
                        <guid isPermaLink="true">https://cyclesresearchinstitute.org/community/methods/kotovs-method-app/</guid>
                    </item>
				                    <item>
                        <title>A. E. Douglass: Tree Rings, Sunspots, and the Birth of Dendrochronology</title>
                        <link>https://cyclesresearchinstitute.org/community/botany/a-e-douglass-tree-rings-sunspots-and-the-birth-of-dendrochronology/</link>
                        <pubDate>Sat, 29 Aug 2026 22:51:14 +0000</pubDate>
                        <description><![CDATA[A. E. Douglass: Tree Rings, Sunspots, and the Birth of Dendrochronology
Compiled from the Foundation for the Study of Cycles archive (Cycles Magazine, vols. 1950–1957)
Who He Was
The arch...]]></description>
                        <content:encoded><![CDATA[<h1 class="western">A. E. Douglass: Tree Rings, Sunspots, and the Birth of Dendrochronology</h1>
<p><i>Compiled from the Foundation for the Study of Cycles archive (Cycles Magazine, vols. 1950–1957)</i></p>
<h2 class="western">Who He Was</h2>
<p>The archive gives a clear, specific origin story for Douglass, drawn from a secondary source the Foundation reviewed in its 1952 volume — a <i>Scientific American</i> article by J. H. Bush (a physicist at the High Altitude Observatory of Harvard University and the University of Colorado):</p>
<blockquote>"This article concerns itself chiefly with the tree ring dating work of A. E. Douglass of the University of Arizona. In 1901 Douglass, then assistant astronomer at the Lowell Observatory, first studied tree ring widths — which are available over long periods of years and which seem to be associated with sunspots — in an effort to throw light on probable solar activity prior to the time when systematic observations of sunspots were first commenced."</blockquote>
<p>So Douglass started as an <b>astronomer</b>, not a botanist or forester — his interest in tree rings was originally a workaround, a way to extend the sunspot record backward in time before telescopic sunspot observation began in 1610. That astronomical motivation is the thread running through everything else he did.</p>
<p>By later mentions in the archive he's affiliated with the <b>Steward Observatory</b> in Arizona (a separate 1957 reference lists him as a correspondent alongside directors of the Mount Wilson Solar Observatory, the Zurich Observatory, and the Royal Observatory at Greenwich — suggesting Douglass was regarded internationally as a peer authority on solar-terrestrial cycles by the 1950s).</p>
<p><b>Verification note:</b> as with the other entries, this is what the magazine states directly. Douglass's broader reputation as the founder of the field now called dendrochronology, and his work founding the Laboratory of Tree-Ring Research at the University of Arizona, is well-established history outside this archive — but that detail is not itself sourced from Cycles Magazine and would need independent confirmation if you want to include it.</p>
<h2 class="western">The Method</h2>
<p>Douglass's insight — that tree rings vary in width from year to year in ways that track climate, and that this pattern could be used to extend records of solar activity and climate far beyond the reach of direct observation — turned him into probably the single most frequently cited data source in the entire archive. Almost every cycle-length discussion in the magazine eventually calls on Douglass's tree-ring measurements as a cross-check.</p>
<h2 class="western">Specific Cycle Lengths Attributed to Douglass</h2>
<p>The range of cycle lengths credited to Douglass across the archive is remarkable — he's cited on nearly every rhythm the Foundation studied, from just over 8 years to multi-decade cycles:</p>
<ul>
<li>
<p><b>~8.2 years</b> — thickness/thinness variation in tree rings at Cibecue, Arizona (though the 1953 volume notes this cycle wasn't found in Arizona tree rings generally, only at this specific site)</p>
</li>
<li>
<p><b>~9.1 years</b> — tree rings at Flagstaff, Arizona</p>
</li>
<li>
<p><b>~9.2 years</b> — tree rings at Santa Catalina, Arizona</p>
</li>
<li>
<p><b>~12.0–12.1 years</b> — tree rings in Arizona pines near the Grand Canyon</p>
</li>
<li>
<p><b>~14⅔ years</b> — described in the 1952 volume as part of a striking multi-domain convergence: C. N. Anderson found this length in sunspots, F. A. Pearson found it in pepper prices and cattle purchasing power, and Douglass found "what seems to be the same rhythm in the alternate thickness and thinness of tree rings" — with the crests of the Anderson, Pearson, and Douglass waves reportedly falling at roughly the same time</p>
</li>
<li>
<p><b>~16⅔ years</b> — Arizona and Java tree rings, cross-referenced against wrought iron prices in England</p>
</li>
<li>
<p><b>~17.7 years (17¾)</b> — one of the archive's most extensively cross-validated cycles, found by Douglass in Arizona tree ring widths (A.D. 931–1939) and matched against pig iron prices, cotton prices, sunspots, war intensity (Wheeler's index), and even Chinese earthquake records back to A.D. 54</p>
</li>
<li>
<p><b>~54 years</b> — the Foundation notes it extended Beveridge's wheat-price work using Douglass's tree-ring record, finding "average waves of this length in Arizona tree rings back for 1084 years" — an extraordinary span for a single continuous proxy record</p>
</li>
</ul>
<h2 class="western">The 1084-Year Tree-Ring Record</h2>
<p>That last item deserves its own emphasis. The Foundation's own text states plainly that Arizona tree-ring data (drawing on Douglass's methodology, whether from his own direct measurements or built on his techniques) extended back <b>1,084 years</b> — meaning cycles researchers in the 1950s had access to a continuous, year-by-year climate proxy stretching back to roughly the 9th century A.D. This is what made Douglass's tree-ring chronology so valuable to the Foundation: nearly everything else in their archive (war records, price data, even sunspot counts) has a much shorter continuous history. Tree rings gave them a way to test whether short-term cycles found in modern data also held up across a millennium.</p>
<h2 class="western">Founding Role in the Field</h2>
<p>The 1952 volume's review of the Bush article makes clear that Douglass's approach eventually grew into an entire discipline. While the term "dendrochronology" itself doesn't appear in the archive text retrieved here, the method described — using tree-ring widths as a dating and climate-reconstruction tool — is exactly that field, and Douglass is now generally credited outside this archive as its founder. His work is also referenced in the 1957 volume under the title <i>"Climatic Cycles and Tree Growth,"</i> associated with the Steward Observatory.</p>
<h2 class="western">Why He Mattered to the Foundation</h2>
<p>Where Brunt and Beveridge gave the Foundation two rich but historically bounded datasets (roughly a century and roughly nine centuries of records, respectively), Douglass gave them something rarer: a natural archive that could, in principle, be extended arbitrarily far back in time, tree by tree, without needing anyone to have kept written records. For an organization obsessed with proving that cycles found in modern data weren't statistical flukes, a millennium-plus of independent tree-ring evidence was about as strong a form of corroboration as existed anywhere in their toolkit.</p>
<h2 class="western">Suggested Forum Angle</h2>
<p>Douglass is a good complement to Beveridge and Brunt for exactly the reason above: those two represent the ceiling of what documentary/historical records could offer (centuries), while Douglass represents an entirely different kind of evidence — physical, biological, and capable of reaching back over a thousand years. A forum piece contrasting these three approaches to "how far back can you actually check a cycle" could be a strong follow-up to the individual biographical entries.</p>
<p>&nbsp;</p>]]></content:encoded>
						                            <category domain="https://cyclesresearchinstitute.org/community/"></category>                        <dc:creator>RayTomes</dc:creator>
                        <guid isPermaLink="true">https://cyclesresearchinstitute.org/community/botany/a-e-douglass-tree-rings-sunspots-and-the-birth-of-dendrochronology/</guid>
                    </item>
							        </channel>
        </rss>
		