Skip to content

Instantly share code, notes, and snippets.

@BiatuAutMiahn
Last active May 15, 2025 20:52
Show Gist options
  • Save BiatuAutMiahn/f133ad52c8ac17271e1d6c1b4fe2a0c7 to your computer and use it in GitHub Desktop.
Save BiatuAutMiahn/f133ad52c8ac17271e1d6c1b4fe2a0c7 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DigitalRootCompass</title>
<style>
body {
font-family: sans-serif;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
background-color: #f0f0f0;
}
#graph-container {
display: flex;
align-items: flex-end; /* Align bars at the bottom */
border: 1px solid #ccc;
padding: 10px;
height: 300px; /* Max height for graph area */
background-color: white;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
margin-bottom: 20px;
overflow-x: auto; /* Allows horizontal scrolling for many columns */
width: 90%; /* Adjust if needed */
max-width: 1200px; /* Max width for the graph container */
}
.column {
display: flex;
flex-direction: column;
align-items: center;
margin: 0 3px; /* Slightly reduced margin */
/* width: 50px; */ /* REMOVED - Will be set by JS */
flex-shrink: 0; /* Prevent columns from shrinking */
}
.bar {
width: 100%; /* Bar takes full width of its column */
background-color: steelblue;
transition: height 0.1s linear, background-color 0.1s linear;
height: 0;
}
.label, .count {
margin-top: 5px;
font-size: 10px; /* Slightly smaller for narrower columns */
white-space: nowrap; /* Prevent label text from wrapping */
}
.count {
font-weight: bold;
color: #333;
}
.controls {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 10px;
}
.controls button, .controls select, .controls label {
padding: 10px 15px;
margin: 5px;
cursor: pointer;
border: 1px solid #ccc;
background-color: #e9e9e9;
}
.controls button:hover, .controls select:hover {
background-color: #dcdcdc;
}
.controls label {
border: none;
background-color: transparent;
padding: 0;
margin-right: -10px;
}
</style>
</head>
<body>
<h1>Digital Root Compass</h1>
<p>Your flything through time, which path are you taking? You are a Kite with a string in a hurricane...good luck.</p>
<div id="graph-container">
<!-- Columns will be generated by JavaScript -->
</div>
<div class="controls">
<button id="startButton">Start</button>
<button id="stopButton">Stop</button>
<button id="resetButton">Reset</button>
<label for="baseSelector">Base:</label>
<select id="baseSelector">
<option value="10">Base 10</option>
<option value="2">Base 2</option>
<option value="8">Base 8</option>
<option value="16">Base 16</option>
<option value="32">Base 32</option>
<option value="64">Base 64</option>
<option value="128">Base 128</option>
</select>
</div>
<script>
let dataStore = {};
const graphContainer = document.getElementById('graph-container');
const MAX_BAR_HEIGHT_PX = 250;
let generatorIntervalId = null;
let discarderIntervalId = null;
let graphUpdateIntervalId = null;
const GENERATION_INTERVAL_MS = 1;
let DISCARD_INTERVAL_MS = 72;
const GRAPH_UPDATE_INTERVAL_MS = 36;
const TARGET_MAX_RANDOM_VALUE = 65529;
// Configuration for dynamic column width
const STANDARD_COLUMN_WIDTH_PX = 45;
const MIN_COLUMN_WIDTH_PX = 15;
const COLUMN_WIDTH_SCALING_THRESHOLD = 10; // Number of columns before we start scaling down
function getSecureRandomInt(maxInclusive) {
const range = maxInclusive + 1;
const maxSourceValue = 0xFFFFFFFF;
const limit = Math.floor((maxSourceValue + 1) / range) * range;
let randomNumber;
const buffer = new Uint32Array(1);
do {
window.crypto.getRandomValues(buffer);
randomNumber = buffer[0];
} while (randomNumber >= limit);
return randomNumber % range;
}
function getCurrentBase() {
const selector = document.getElementById('baseSelector');
return selector ? parseInt(selector.value) : 10;
}
function generateAndAppend() {
let randomNumber;
if (window.crypto && window.crypto.getRandomValues) {
randomNumber = getSecureRandomInt(TARGET_MAX_RANDOM_VALUE);
} else {
console.error("Cryptographically secure random number generator not available. Stopping.");
stopSimulation();
return;
}
const currentBase = getCurrentBase();
const root = getDigitalRoot(randomNumber, currentBase);
const key = root.toString();
if (dataStore[key]) {
dataStore[key].push(randomNumber);
} else {
// This might happen if base changes and initialize hasn't perfectly caught up
// Or if an invalid root is somehow generated.
// console.warn(`Key ${key} not found in dataStore for base ${currentBase}. Number: ${randomNumber}, Root: ${root}`);
}
}
function initialize() {
graphContainer.innerHTML = '';
dataStore = {};
const currentBase = getCurrentBase();
const numColumns = (currentBase - 1 === 0) ? 1 : (currentBase - 1);
if(currentBase>1){
DISCARD_INTERVAL_MS=72*numColumns;
}
// Calculate dynamic column width
let columnWidthPx = STANDARD_COLUMN_WIDTH_PX;
if (numColumns > COLUMN_WIDTH_SCALING_THRESHOLD) {
columnWidthPx = Math.max(
MIN_COLUMN_WIDTH_PX,
STANDARD_COLUMN_WIDTH_PX * (COLUMN_WIDTH_SCALING_THRESHOLD / numColumns)
);
}
for (let i = 1; i <= numColumns; i++) {
const key = i.toString();
dataStore[key] = [];
const columnDiv = document.createElement('div');
columnDiv.classList.add('column');
columnDiv.style.width = `${Math.floor(columnWidthPx)}px`; // Apply calculated width
const countSpan = document.createElement('span');
countSpan.classList.add('count');
countSpan.id = `count-${key}`;
countSpan.textContent = '0';
const barDiv = document.createElement('div');
barDiv.classList.add('bar');
barDiv.id = `bar-${key}`;
barDiv.style.height = '0px';
const labelSpan = document.createElement('span');
labelSpan.classList.add('label');
labelSpan.textContent = key;
columnDiv.appendChild(countSpan);
columnDiv.appendChild(barDiv);
columnDiv.appendChild(labelSpan);
graphContainer.appendChild(columnDiv);
}
}
function getDigitalRoot(num, base) {
if (base <= 1) {
console.warn("Base must be greater than 1. Defaulting to 10 for root calculation.");
base = 10;
}
const modulus = base - 1;
if (modulus === 0) { // Base 2
return 1;
}
if (num === 0) return modulus;
let root = num % modulus;
return root === 0 ? modulus : root;
}
function discardOldest() {
const currentBase = getCurrentBase();
const numColumns = (currentBase - 1 === 0) ? 1 : (currentBase - 1);
for (let i = 1; i <= numColumns; i++) {
const key = i.toString();
if (dataStore[key] && dataStore[key].length > 0) {
dataStore[key].shift();
}
}
}
function updateGraph() {
const currentBase = getCurrentBase();
const numColumns = (currentBase - 1 === 0) ? 1 : (currentBase - 1);
let overallMaxCount = 0;
let maxCountColumns = [];
for (let i = 1; i <= numColumns; i++) {
const key = i.toString();
// Ensure the key exists in dataStore before trying to access length
const count = dataStore[key] ? dataStore[key].length : 0;
if (count > overallMaxCount) {
overallMaxCount = count;
maxCountColumns = [key];
} else if (count === overallMaxCount && overallMaxCount > 0) {
maxCountColumns.push(key);
}
}
const scalingFactor = overallMaxCount > 0 ? MAX_BAR_HEIGHT_PX / overallMaxCount : 0;
for (let i = 1; i <= numColumns; i++) {
const key = i.toString();
// Ensure dataStore[key] exists before trying to access its length
const count = dataStore[key] ? dataStore[key].length : 0;
const barElement = document.getElementById(`bar-${key}`);
const countElement = document.getElementById(`count-${key}`);
if (barElement && countElement) {
const barHeight = Math.min(MAX_BAR_HEIGHT_PX, count * scalingFactor);
barElement.style.height = `${barHeight}px`;
countElement.textContent = count;
if (maxCountColumns.includes(key)) {
barElement.style.backgroundColor = 'green';
} else {
barElement.style.backgroundColor = 'steelblue';
}
}
}
}
function startSimulation() {
if (generatorIntervalId !== null) return;
console.log('Starting simulation...');
initialize();
updateGraph();
generatorIntervalId = setInterval(generateAndAppend, GENERATION_INTERVAL_MS);
discarderIntervalId = setInterval(discardOldest, DISCARD_INTERVAL_MS);
graphUpdateIntervalId = setInterval(updateGraph, GRAPH_UPDATE_INTERVAL_MS);
document.getElementById('startButton').disabled = true;
document.getElementById('stopButton').disabled = false;
document.getElementById('resetButton').disabled = false;
document.getElementById('baseSelector').disabled = true;
}
function stopSimulation() {
if (generatorIntervalId === null) return;
console.log('Stopping simulation...');
clearInterval(generatorIntervalId);
clearInterval(discarderIntervalId);
clearInterval(graphUpdateIntervalId);
generatorIntervalId = null;
discarderIntervalId = null;
graphUpdateIntervalId = null;
document.getElementById('startButton').disabled = false;
document.getElementById('stopButton').disabled = true;
document.getElementById('resetButton').disabled = false;
document.getElementById('baseSelector').disabled = false;
}
function resetDataAndGraph() {
console.log('Resetting data and graph...');
// Initialize will clear dataStore, read current base, and rebuild columns with new widths
initialize();
updateGraph();
}
document.getElementById('startButton').addEventListener('click', startSimulation);
document.getElementById('stopButton').addEventListener('click', stopSimulation);
document.getElementById('resetButton').addEventListener('click', resetDataAndGraph);
document.getElementById('baseSelector').addEventListener('change', () => {
if (generatorIntervalId !== null) {
stopSimulation();
}
initialize(); // This will now also recalculate and apply column widths
updateGraph();
document.getElementById('startButton').disabled = false;
document.getElementById('stopButton').disabled = true;
});
document.addEventListener('DOMContentLoaded', () => {
initialize(); // Initial setup with dynamic column width
updateGraph();
document.getElementById('stopButton').disabled = true;
document.getElementById('resetButton').disabled = false;
document.getElementById('baseSelector').disabled = false;
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment