PulseCore

Chapter 3 · Section 16

Gravity Rings: RingField and the Adaptive Ring Search

The BPT law

BPT Part 2.8 defines the beat the rings keep: the Local Universe Time Crystal duration is "the Pulse Rhythm: the time to cross the Pulse Diameter one way," ⧖⌂ = ↁℨ⧖ × 2ⁿ × f₀ⁿ × ⟪C⟫. BPT Part 7.5 (The Resonant Field of Becoming, Interference Dynamics) gives how pulses add where they meet: the Constructive Interference Amplitude, A⋓ = |Σ_i A_i e^(iφ_i)|, which is greatest "when phase relationships satisfy integer multiples of 2π (φ_i − φ_j = 2πn)", with destructive interference at (2n + 1)π making null zones of radius r∅ = λ/4. The game's rings join the two. The comment at rings.js:4–15 reads: "Every well of a universe pulses, and each pulse sends a ring outward at one cell per pulse: the Pulse Diameter per Rhythm." It cites BPT Part 2.8 for the Pulse Rhythm as the Time Crystal duration, Chapter 7 for interference and harmonics, and Chapter 5 for phase coupling. Where k rings meet in the same frame is a convergence of depth k: 2 at a vesica tip, 3 where three petals touch, 6 at a daisy's center (the flower of life). That each well sends rings is the game's reading of the Pulse Rhythm; BPT does not state rings from wells as such. Their search is a game method.

The established physics

None: a game method. Two circles meet at zero, one or two points, by plane geometry (circleMeets() rings.js:70).

The game's math

ensureRings() app.js:565 builds the field:

new RingField({ period: pulseLength = 2, speed: 1, maxRadius: max(rows, cols)/2,
                eps: 0.35, maxRings: 10, maxSources: max(4000, ⌊rows·cols/4⌋), ringSources: 32 })
  • Rings of a well, radii(src, t) rings.js:42: one ring per period since birth, radius (age − k·period)·speed. They stop at maxRadius or at the newest maxRings.
  • A ring through a point, ringThrough() rings.js:55. A ring of radius d left at age − d/speed. It exists if that time is within eps/speed of a multiple of the period.
  • The search, step(t) rings.js:87. It takes the youngest ringSources wells. For each pair, and each pair of their rings with both radii at least 2, it finds where the circles meet. Each meeting is checked against every other well's rings, and the depth is the count passing through. A meeting that recurs at the same rounded spot, seen on three beats with depth ≥ 2, is announced as a time crystal (a node). A crystal unseen for 4 periods is forgotten. alive(t) returns crystals seen within one period. The search is O(n²·r²·n) in the n wells searched.
  • The adaptive count, stepFinds() app.js:1918–1923. The search is timed each step. If it took under 4 ms and more wells exist than are searched, ringSources grows by 15 %. If it took over 8 ms, it shrinks by 15 %, never below 8 (ruling 2026-10-29: "swap that"; it was a fixed 32).
  • Node finds are off. NODE_FINDS = false (app.js:231, ruling 2026-10-28): the rings still meet and are drawn, but their meetings are not collected.

The sources are the wells. rings.sources is the game's list of physical wells and sites, which every other Part reads.

In the game

The rings are drawn in the 2D view ("2D · the field" in the view menu, index.php:141), from the big bang on: every well and every site sends thin cyan circles out from its center, one a Tempo, fainter as they widen (drawGrid() app.js:4096–4101). Where rings meet in step the live crystals show as small diamonds, colored by their rarity's hue and larger for a greater depth, 3 + 1.5·depth pixels (app.js:4104–4107). The drawer switch "Show the wells' rings" (index.php:194, on by default) turns the circles off in 2D; in 3D the same switch shows or hides the halo ring round each well and each Proto Well (scene3d.js:1526, 1646). The 3D view does not draw the expanding rings. Its crystal markers, small glowing octahedra, at most 120 (setCrystals() scene3d.js:1773–1781), are cleared once the matter is drawn (scene3d.js:1775), and the rings only begin at the big bang, when the first wells and sites join rings.sources, so in 3D the meetings are not marked. The help card "Finds" tells the player: "Every well sends rings; where rings meet in step a harmonic node forms, deeper where more meet" (index.php:309). While NODE_FINDS is off the meetings are drawn but no node is added to the Collection.

The scale model

The rings are drawn in drawn cells and flips. One ring per Tempo per drawn well. The ring search covers only the youngest ringSources drawn wells, so convergences among older wells are not found. That only bears on what is found, not on any physics.

Where the game and the theory differ

  • addSource() rings.js:34 still drops the oldest source when the list passes maxSources: if (this.sources.length > this.maxSources) this.sources.shift(). The comment at rings.js:26–27 says sources "are no longer dropped". That holds only while the list stays under maxSources = max(4,000, ⌊rows·cols/4⌋): 4,000 on a phone and 16,384 on a desktop. On a desktop at level 202, the comment at matter.js:245 counts about 15,000 sites, which leaves room for fewer than 1,400 more wells. Past that, the oldest well or site is silently removed from gravity, the census and the lineage.
  • The rings live on a torus, but circleMeets() rings.js:70 and ringThrough() rings.js:55 use plain plane distance, and step() skips meetings that fall outside the field (rings.js:101), so rings that meet across the seam are not found. The rings also stop at half the field, maxRadius = max(rows, cols)/2.

Fixed numbers

Number Where What it is Kind
2 pulseLength app.js:51 period: a ring per Tempo, two flips BPT (Pulse Rhythm, Part 2.8)
1 app.js:565 ring speed, a cell a flip BPT (Pulse Diameter per Rhythm, Part 2.8)
1/2 app.js:565 maxRadius, half the field CAP (a fixed reach; the meeting geometry ignores the wrap, so half the field is where it stops being right)
0.35 app.js:565 eps, how close a ring must pass METHOD (tolerance on a rasterized ring)
10 app.js:565 maxRings, newest rings checked CAP (older rings are not searched)
4000, 1/4 app.js:565 maxSources CAP (past it the oldest well is dropped from the game)
32 app.js:565 ringSources at start CAP (the starting search count)
3, 400, 0.6, 6, 24, ∞ rings.js:19 constructor defaults, all overridden by ensureRings() CAP (not in effect)
2 rings.js:99 rings under radius 2 skipped: "right on a well is the well" METHOD
1e-9 rings.js:79 tangent test METHOD
3 rings.js:126 beats seen before a crystal is announced METHOD (a pattern must repeat)
2 rings.js:126 least depth of a crystal BPT (a vesica, two rings; rings.js:13)
4 rings.js:129 periods before a crystal is forgotten RECORD
1 rings.js:133 crystal alive within one period DISPLAY
4 ms, 8 ms app.js:1922–1923 search time budget CAP (a device-time budget on the wells searched)
1.15, 0.85 app.js:1922–1923 growth and shrink steps METHOD (control-loop gains)
8 app.js:1923 least wells searched CAP
12 app.js:1934 pulses between node pops (inert while NODE_FINDS is false) DISPLAY
3, 1.5 drawGrid() app.js:4105 2D crystal diamond size, 3 + 1.5·depth px DISPLAY
120 setCrystals() scene3d.js:1777 crystal markers drawn in 3D DISPLAY