This document is a snapshot manual for the first version of the SheepCounter
architecture. It is designed as an anchor point before the system grows more
complex. Future milestones can have their own manuals (in2_*.html, in3_*.html, …),
but this file preserves the barebone abstract counter API and its initial design
considerations.
The SheepCounter system is a timeline engine and number system framework. It is designed to:
t?”The current implementation uses a simple example (counting 0–10 with equal durations) as a placeholder. The design is what matters: it must be easy to replace the example with real Laegna logic without changing the overall architecture.
The core idea is that everything that occupies time is a block. This is captured
by the base class SheepBlock. All other time-based entities inherit from it:
SheepPadding – explicit gaps or transitions.SheepNumber – one number instance.SheepNumberSystem – a sequence of numbers.SheepSubChapter – one (type + R) chapter.SheepMainChapter – a family of subchapters (e.g. all Frequential R=1..4).SheepCounter – the global orchestrator.Each block has:
startTimeendTimelengthEach number has up to five logical “screens”:
d0, d1, d2 – auxiliary displays.dm – main display.ds – secondary display.
Each screen is a SheepDisplay object with:
titlecontentvisibleThe hierarchy is:
SheepCounter is the main entry point. It:
SheepTOC definition.getTotalLength()getTOCStructure()getNumberAtTime(t)getChapterStartTime(typeCode, rCode)Each section below describes one class or structure. It includes:
SheepBlock is the abstract base class for anything that occupies time on the global timeline.
Fields:
startTime, endTime, lengthMethods:
setStartTime(t), containsTime(t), toDebugJSON()
// **SheepBlock** – base class for anything on the timeline
class SheepBlock {
constructor() {
this.startTime = 0;
this.endTime = 0;
this.length = 0;
}
setStartTime(t) {
this.startTime = t;
this.endTime = t + this.length;
}
containsTime(t) {
return t >= this.startTime && t < this.endTime;
}
toDebugJSON() {
return {
type: "block",
startTime: this.startTime,
endTime: this.endTime,
length: this.length
};
}
}
SheepPadding represents non-number time segments: intros, outros, pauses, transitions.
// **SheepPadding** – timeline atom for gaps
class SheepPadding extends SheepBlock {
constructor(length) {
super();
this.length = length;
}
toDebugJSON() {
return {
type: "padding",
startTime: this.startTime,
endTime: this.endTime,
length: this.length
};
}
}
SheepDisplay represents one “screen” of information for a number.
// **SheepDisplay** – single display (d0, d1, d2, dm, ds)
class SheepDisplay {
constructor(codeName, title = "", content = "") {
this.codeName = codeName; // "d0", "d1", "d2", "dm", "ds"
this.title = title;
this.content = content;
this.visible = true;
}
set(title, content, visible = true) {
this.title = title;
this.content = content;
this.visible = visible;
}
toDebugJSON() {
return {
codeName: this.codeName,
title: this.title,
content: this.content,
visible: this.visible
};
}
}
SheepNumber represents a single number in a given number system.
// **SheepNumber** – one number instance in a system
class SheepNumber extends SheepBlock {
constructor(system, index, valueStr, length = 1.0) {
super();
this.system = system;
this.index = index;
this.valueStr = valueStr;
this.length = length;
this.displays = {
d0: new SheepDisplay("d0", "Aux 0", valueStr),
d1: new SheepDisplay("d1", "Aux 1", valueStr),
d2: new SheepDisplay("d2", "Aux 2", valueStr),
dm: new SheepDisplay("dm", "Main", valueStr),
ds: new SheepDisplay("ds", "Secondary", valueStr)
};
}
getLength() {
return this.length;
}
toDebugJSON() {
return {
type: "number",
index: this.index,
valueStr: this.valueStr,
startTime: this.startTime,
endTime: this.endTime,
length: this.length,
displays: Object.fromEntries(
Object.entries(this.displays).map(([k, d]) => [k, d.toDebugJSON()])
)
};
}
}
SheepNumberSystem represents a full sequence of numbers in a given Laegna system.
// **SheepNumberSystem** – e.g. Frequential / Octavian
class SheepNumberSystem extends SheepBlock {
constructor(typeCode, rCode) {
super();
this.typeCode = typeCode;
this.rCode = rCode;
this.paddingBefore = new SheepPadding(0.0);
this.paddingAfter = new SheepPadding(0.0);
this.numbers = [];
this.numberStartTimes = [];
this._initNumbers();
this._buildIndex();
}
_initNumbers() {
const count = 11;
for (let i = 0; i < count; i++) {
const valueStr = String(i);
const num = new SheepNumber(this, i, valueStr, 1.0);
this.numbers.push(num);
}
}
_buildIndex() {
let t = 0;
this.numberStartTimes = [];
for (const num of this.numbers) {
num.setStartTime(t);
this.numberStartTimes.push(t);
t += num.getLength();
}
this.length = this.paddingBefore.length + t + this.paddingAfter.length;
}
setStartTime(t) {
super.setStartTime(t);
let cursor = this.startTime + this.paddingBefore.length;
this.paddingBefore.setStartTime(this.startTime);
for (const num of this.numbers) {
num.setStartTime(cursor);
cursor += num.getLength();
}
this.paddingAfter.setStartTime(cursor);
}
getNumberAtTime(t) {
if (!this.containsTime(t)) return null;
const local = t - this.startTime - this.paddingBefore.length;
if (local < 0 || local >= (this.length - this.paddingBefore.length - this.paddingAfter.length)) {
return null;
}
for (const num of this.numbers) {
if (num.containsTime(t)) {
return { number: num, localTime: t - num.startTime };
}
}
return null;
}
toDebugJSON() {
return {
type: "numberSystem",
typeCode: this.typeCode,
rCode: this.rCode,
startTime: this.startTime,
endTime: this.endTime,
length: this.length,
paddingBefore: this.paddingBefore.toDebugJSON(),
paddingAfter: this.paddingAfter.toDebugJSON(),
numbers: this.numbers.map(n => n.toDebugJSON())
};
}
}
SheepSubChapter wraps a SheepNumberSystem with chapter-level padding.
// **SheepSubChapter** – one (type + R) chapter
class SheepSubChapter extends SheepBlock {
constructor(mainChapter, typeCode, rCode, displayRName) {
super();
this.mainChapter = mainChapter;
this.typeCode = typeCode;
this.rCode = rCode;
this.displayRName = displayRName;
this.paddingBefore = new SheepPadding(0.0);
this.paddingAfter = new SheepPadding(0.0);
this.system = null;
}
initSystem(system) {
this.system = system;
this.length = this.paddingBefore.length + system.length + this.paddingAfter.length;
}
setStartTime(t) {
super.setStartTime(t);
this.paddingBefore.setStartTime(this.startTime);
const sysStart = this.startTime + this.paddingBefore.length;
this.system.setStartTime(sysStart);
const afterStart = sysStart + this.system.length;
this.paddingAfter.setStartTime(afterStart);
}
getNumberAtTime(t) {
if (!this.containsTime(t)) return null;
if (!this.system) return null;
return this.system.getNumberAtTime(t);
}
toDebugJSON() {
return {
type: "subChapter",
typeCode: this.typeCode,
rCode: this.rCode,
displayRName: this.displayRName,
startTime: this.startTime,
endTime: this.endTime,
length: this.length,
paddingBefore: this.paddingBefore.toDebugJSON(),
paddingAfter: this.paddingAfter.toDebugJSON(),
system: this.system ? this.system.toDebugJSON() : null
};
}
}
SheepMainChapter groups subchapters for one number system type.
// **SheepMainChapter** – Frequential / Octavian
class SheepMainChapter extends SheepBlock {
constructor(typeCode, displayName) {
super();
this.typeCode = typeCode;
this.displayName = displayName;
this.paddingBefore = new SheepPadding(0.0);
this.paddingAfter = new SheepPadding(0.0);
this.subChapters = [];
}
addSubChapter(sub) {
this.subChapters.push(sub);
}
buildTimeline() {
let t = this.startTime + this.paddingBefore.length;
this.paddingBefore.setStartTime(this.startTime);
for (const sub of this.subChapters) {
sub.setStartTime(t);
t += sub.length;
}
this.paddingAfter.setStartTime(t);
this.length = this.paddingBefore.length +
this.subChapters.reduce((acc, s) => acc + s.length, 0) +
this.paddingAfter.length;
this.endTime = this.startTime + this.length;
}
getNumberAtTime(t) {
if (!this.containsTime(t)) return null;
for (const sub of this.subChapters) {
const res = sub.getNumberAtTime(t);
if (res) {
return {
mainChapter: this,
subChapter: sub,
number: res.number,
localTimeInNumber: res.localTime
};
}
}
return null;
}
toDebugJSON() {
return {
type: "mainChapter",
typeCode: this.typeCode,
displayName: this.displayName,
startTime: this.startTime,
endTime: this.endTime,
length: this.length,
paddingBefore: this.paddingBefore.toDebugJSON(),
paddingAfter: this.paddingAfter.toDebugJSON(),
subChapters: this.subChapters.map(s => s.toDebugJSON())
};
}
}
SheepTOC defines which main chapters and R-levels exist.
// **SheepTOC** – defines structure
class SheepTOC {
constructor() {
this.mainDefs = [];
this._initDefs();
}
_initDefs() {
const types = [
{ typeCode: "FREQUENTIAL", displayName: "Frequential Numbers" },
{ typeCode: "OCTAVIAN", displayName: "Octavian Numbers" }
];
const rCodes = [
{ rCode: "R0", displayRName: "R=1" },
{ rCode: "R1", displayRName: "R=2" },
{ rCode: "R2", displayRName: "R=3" },
{ rCode: "R3", displayRName: "R=4" }
];
for (const t of types) {
this.mainDefs.push({
typeCode: t.typeCode,
displayName: t.displayName,
rDefs: rCodes.map(r => ({
rCode: r.rCode,
displayRName: r.displayRName
}))
});
}
}
getDefs() {
return this.mainDefs;
}
}
SheepCounter builds the whole structure and exposes the main API.
// **SheepCounter** – global timeline and API
class SheepCounter extends SheepBlock {
constructor() {
super();
this.paddingBefore = new SheepPadding(0.0);
this.paddingAfter = new SheepPadding(0.0);
this.toc = new SheepTOC();
this.mainChapters = [];
this._initChapters();
this._buildTimeline();
}
_initChapters() {
const defs = this.toc.getDefs();
for (const mainDef of defs) {
const main = new SheepMainChapter(mainDef.typeCode, mainDef.displayName);
for (const rDef of mainDef.rDefs) {
const sub = new SheepSubChapter(
main,
mainDef.typeCode,
rDef.rCode,
rDef.displayRName
);
const system = new SheepNumberSystem(mainDef.typeCode, rDef.rCode);
sub.initSystem(system);
main.addSubChapter(sub);
}
this.mainChapters.push(main);
}
}
_buildTimeline() {
let t = this.startTime + this.paddingBefore.length;
this.paddingBefore.setStartTime(this.startTime);
for (const main of this.mainChapters) {
main.setStartTime(t);
main.buildTimeline();
t += main.length;
}
this.paddingAfter.setStartTime(t);
this.length = this.paddingBefore.length +
this.mainChapters.reduce((acc, m) => acc + m.length, 0) +
this.paddingAfter.length;
this.endTime = this.startTime + this.length;
}
getTotalLength() {
return this.length;
}
getTOCStructure() {
return this.mainChapters.map(main => ({
typeCode: main.typeCode,
displayName: main.displayName,
mainChapter: main,
subChapters: main.subChapters.map(sub => ({
rCode: sub.rCode,
displayRName: sub.displayRName,
subChapter: sub
}))
}));
}
getNumberAtTime(t) {
if (!this.containsTime(t)) return null;
for (const main of this.mainChapters) {
const res = main.getNumberAtTime(t);
if (res) {
return {
mainChapter: res.mainChapter,
subChapter: res.subChapter,
number: res.number,
localTimeInNumber: res.localTimeInNumber
};
}
}
return null;
}
getChapterStartTime(typeCode, rCode) {
for (const main of this.mainChapters) {
if (main.typeCode !== typeCode) continue;
for (const sub of main.subChapters) {
if (sub.rCode === rCode) {
return sub.startTime;
}
}
}
return null;
}
toDebugJSON() {
return {
type: "counter",
startTime: this.startTime,
endTime: this.endTime,
length: this.length,
paddingBefore: this.paddingBefore.toDebugJSON(),
paddingAfter: this.paddingAfter.toDebugJSON(),
mainChapters: this.mainChapters.map(m => m.toDebugJSON())
};
}
}
This section contains the entire barebone sheepcounter.js code as used in this
manual. It is collapsed by default to keep the document readable.
// (Same content as the class definitions above, grouped in one file)
class SheepBlock {
constructor() { this.startTime = 0; this.endTime = 0; this.length = 0; }
setStartTime(t) { this.startTime = t; this.endTime = t + this.length; }
containsTime(t) { return t >= this.startTime && t < this.endTime; }
toDebugJSON() { return { type: "block", startTime: this.startTime, endTime: this.endTime, length: this.length }; }
}
class SheepPadding extends SheepBlock {
constructor(length) { super(); this.length = length; }
toDebugJSON() { return { type: "padding", startTime: this.startTime, endTime: this.endTime, length: this.length }; }
}
class SheepDisplay {
constructor(codeName, title = "", content = "") {
this.codeName = codeName; this.title = title; this.content = content; this.visible = true;
}
set(title, content, visible = true) { this.title = title; this.content = content; this.visible = visible; }
toDebugJSON() { return { codeName: this.codeName, title: this.title, content: this.content, visible: this.visible }; }
}
class SheepNumber extends SheepBlock {
constructor(system, index, valueStr, length = 1.0) {
super();
this.system = system; this.index = index; this.valueStr = valueStr; this.length = length;
this.displays = {
d0: new SheepDisplay("d0", "Aux 0", valueStr),
d1: new SheepDisplay("d1", "Aux 1", valueStr),
d2: new SheepDisplay("d2", "Aux 2", valueStr),
dm: new SheepDisplay("dm", "Main", valueStr),
ds: new SheepDisplay("ds", "Secondary", valueStr)
};
}
getLength() { return this.length; }
toDebugJSON() {
return {
type: "number",
index: this.index,
valueStr: this.valueStr,
startTime: this.startTime,
endTime: this.endTime,
length: this.length,
displays: Object.fromEntries(
Object.entries(this.displays).map(([k, d]) => [k, d.toDebugJSON()])
)
};
}
}
class SheepNumberSystem extends SheepBlock {
constructor(typeCode, rCode) {
super();
this.typeCode = typeCode; this.rCode = rCode;
this.paddingBefore = new SheepPadding(0.0);
this.paddingAfter = new SheepPadding(0.0);
this.numbers = []; this.numberStartTimes = [];
this._initNumbers(); this._buildIndex();
}
_initNumbers() {
const count = 11;
for (let i = 0; i < count; i++) {
const valueStr = String(i);
const num = new SheepNumber(this, i, valueStr, 1.0);
this.numbers.push(num);
}
}
_buildIndex() {
let t = 0; this.numberStartTimes = [];
for (const num of this.numbers) {
num.setStartTime(t);
this.numberStartTimes.push(t);
t += num.getLength();
}
this.length = this.paddingBefore.length + t + this.paddingAfter.length;
}
setStartTime(t) {
super.setStartTime(t);
let cursor = this.startTime + this.paddingBefore.length;
this.paddingBefore.setStartTime(this.startTime);
for (const num of this.numbers) {
num.setStartTime(cursor);
cursor += num.getLength();
}
this.paddingAfter.setStartTime(cursor);
}
getNumberAtTime(t) {
if (!this.containsTime(t)) return null;
const local = t - this.startTime - this.paddingBefore.length;
if (local < 0 || local >= (this.length - this.paddingBefore.length - this.paddingAfter.length)) return null;
for (const num of this.numbers) {
if (num.containsTime(t)) return { number: num, localTime: t - num.startTime };
}
return null;
}
toDebugJSON() {
return {
type: "numberSystem",
typeCode: this.typeCode,
rCode: this.rCode,
startTime: this.startTime,
endTime: this.endTime,
length: this.length,
paddingBefore: this.paddingBefore.toDebugJSON(),
paddingAfter: this.paddingAfter.toDebugJSON(),
numbers: this.numbers.map(n => n.toDebugJSON())
};
}
}
class SheepSubChapter extends SheepBlock {
constructor(mainChapter, typeCode, rCode, displayRName) {
super();
this.mainChapter = mainChapter; this.typeCode = typeCode; this.rCode = rCode; this.displayRName = displayRName;
this.paddingBefore = new SheepPadding(0.0);
this.paddingAfter = new SheepPadding(0.0);
this.system = null;
}
initSystem(system) {
this.system = system;
this.length = this.paddingBefore.length + system.length + this.paddingAfter.length;
}
setStartTime(t) {
super.setStartTime(t);
this.paddingBefore.setStartTime(this.startTime);
const sysStart = this.startTime + this.paddingBefore.length;
this.system.setStartTime(sysStart);
const afterStart = sysStart + this.system.length;
this.paddingAfter.setStartTime(afterStart);
}
getNumberAtTime(t) {
if (!this.containsTime(t)) return null;
if (!this.system) return null;
return this.system.getNumberAtTime(t);
}
toDebugJSON() {
return {
type: "subChapter",
typeCode: this.typeCode,
rCode: this.rCode,
displayRName: this.displayRName,
startTime: this.startTime,
endTime: this.endTime,
length: this.length,
paddingBefore: this.paddingBefore.toDebugJSON(),
paddingAfter: this.paddingAfter.toDebugJSON(),
system: this.system ? this.system.toDebugJSON() : null
};
}
}
class SheepMainChapter extends SheepBlock {
constructor(typeCode, displayName) {
super();
this.typeCode = typeCode; this.displayName = displayName;
this.paddingBefore = new SheepPadding(0.0);
this.paddingAfter = new SheepPadding(0.0);
this.subChapters = [];
}
addSubChapter(sub) { this.subChapters.push(sub); }
buildTimeline() {
let t = this.startTime + this.paddingBefore.length;
this.paddingBefore.setStartTime(this.startTime);
for (const sub of this.subChapters) {
sub.setStartTime(t);
t += sub.length;
}
this.paddingAfter.setStartTime(t);
this.length = this.paddingBefore.length +
this.subChapters.reduce((acc, s) => acc + s.length, 0) +
this.paddingAfter.length;
this.endTime = this.startTime + this.length;
}
getNumberAtTime(t) {
if (!this.containsTime(t)) return null;
for (const sub of this.subChapters) {
const res = sub.getNumberAtTime(t);
if (res) {
return {
mainChapter: this,
subChapter: sub,
number: res.number,
localTimeInNumber: res.localTime
};
}
}
return null;
}
toDebugJSON() {
return {
type: "mainChapter",
typeCode: this.typeCode,
displayName: this.displayName,
startTime: this.startTime,
endTime: this.endTime,
length: this.length,
paddingBefore: this.paddingBefore.toDebugJSON(),
paddingAfter: this.paddingAfter.toDebugJSON(),
subChapters: this.subChapters.map(s => s.toDebugJSON())
};
}
}
class SheepTOC {
constructor() { this.mainDefs = []; this._initDefs(); }
_initDefs() {
const types = [
{ typeCode: "FREQUENTIAL", displayName: "Frequential Numbers" },
{ typeCode: "OCTAVIAN", displayName: "Octavian Numbers" }
];
const rCodes = [
{ rCode: "R0", displayRName: "R=1" },
{ rCode: "R1", displayRName: "R=2" },
{ rCode: "R2", displayRName: "R=3" },
{ rCode: "R3", displayRName: "R=4" }
];
for (const t of types) {
this.mainDefs.push({
typeCode: t.typeCode,
displayName: t.displayName,
rDefs: rCodes.map(r => ({ rCode: r.rCode, displayRName: r.displayRName }))
});
}
}
getDefs() { return this.mainDefs; }
}
class SheepCounter extends SheepBlock {
constructor() {
super();
this.paddingBefore = new SheepPadding(0.0);
this.paddingAfter = new SheepPadding(0.0);
this.toc = new SheepTOC();
this.mainChapters = [];
this._initChapters();
this._buildTimeline();
}
_initChapters() {
const defs = this.toc.getDefs();
for (const mainDef of defs) {
const main = new SheepMainChapter(mainDef.typeCode, mainDef.displayName);
for (const rDef of mainDef.rDefs) {
const sub = new SheepSubChapter(main, mainDef.typeCode, rDef.rCode, rDef.displayRName);
const system = new SheepNumberSystem(mainDef.typeCode, rDef.rCode);
sub.initSystem(system);
main.addSubChapter(sub);
}
this.mainChapters.push(main);
}
}
_buildTimeline() {
let t = this.startTime + this.paddingBefore.length;
this.paddingBefore.setStartTime(this.startTime);
for (const main of this.mainChapters) {
main.setStartTime(t);
main.buildTimeline();
t += main.length;
}
this.paddingAfter.setStartTime(t);
this.length = this.paddingBefore.length +
this.mainChapters.reduce((acc, m) => acc + m.length, 0) +
this.paddingAfter.length;
this.endTime = this.startTime + this.length;
}
getTotalLength() { return this.length; }
getTOCStructure() {
return this.mainChapters.map(main => ({
typeCode: main.typeCode,
displayName: main.displayName,
mainChapter: main,
subChapters: main.subChapters.map(sub => ({
rCode: sub.rCode,
displayRName: sub.displayRName,
subChapter: sub
}))
}));
}
getNumberAtTime(t) {
if (!this.containsTime(t)) return null;
for (const main of this.mainChapters) {
const res = main.getNumberAtTime(t);
if (res) {
return {
mainChapter: res.mainChapter,
subChapter: res.subChapter,
number: res.number,
localTimeInNumber: res.localTimeInNumber
};
}
}
return null;
}
getChapterStartTime(typeCode, rCode) {
for (const main of this.mainChapters) {
if (main.typeCode !== typeCode) continue;
for (const sub of main.subChapters) {
if (sub.rCode === rCode) return sub.startTime;
}
}
return null;
}
toDebugJSON() {
return {
type: "counter",
startTime: this.startTime,
endTime: this.endTime,
length: this.length,
paddingBefore: this.paddingBefore.toDebugJSON(),
paddingAfter: this.paddingAfter.toDebugJSON(),
mainChapters: this.mainChapters.map(m => m.toDebugJSON())
};
}
}
The real Laegna logic will live mainly in SheepNumberSystem._initNumbers() and
SheepNumber’s behavior over time. The 3D layer should query the counter instead of
reimplementing number logic.
As the system grows, this snapshot remains the reference for the original architecture. Later milestones can diverge in implementation but should keep the same conceptual layering unless there is a deliberate redesign.
This HTML file is a manual + snapshot package. It documents the barebone
SheepCounter architecture and embeds the full JS source as a reference. It is meant to be saved
as in1_sheepcounter.html and kept unchanged as the “intro 1” milestone.
<details>/<summary>.The HTML itself does not execute the SheepCounter classes; they are shown as code blocks. To use the JS in a real project, you would typically put the JS into its own file and then do:
// Example: basic usage and debugging
const counter = new SheepCounter();
// Total length of the whole timeline
console.log("Total length:", counter.getTotalLength());
// Inspect TOC structure
console.log("TOC:", counter.getTOCStructure());
// Inspect which number is active at time t = 5
const stateAt5 = counter.getNumberAtTime(5);
console.log("State at t=5:", stateAt5);
// Jump to a specific chapter (e.g. Frequential R0)
const startFrequentialR0 = counter.getChapterStartTime("FREQUENTIAL", "R0");
console.log("Frequential R0 starts at:", startFrequentialR0);
counter, stateAt5) to see the structure.
The HTML here is mostly WYSIWYG for a programmer: headings, paragraphs, lists, and code blocks.
The manual focuses on the architecture and API, not on explaining what a
<p> tag does. The only HTML-specific behavior worth noting is:
<details>/<summary> for collapsible sections.in2_sheepcounter.html, in3_sheepcounter.html, etc.
This section contains the full HTML source of in1_sheepcounter.html as a single
code block. It is mainly for archival and copy-paste purposes. Note that it includes this
section itself, so it is self-referential.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>in1_sheepcounter – SheepCounter Architecture & Barebone API Manual</title>
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-js.min.js"></script>
<style>
/* (Same CSS as in the <head> of this file) */
</style>
</head>
<body>
</body>
</html>