Last active
May 15, 2025 20:52
-
-
Save BiatuAutMiahn/f133ad52c8ac17271e1d6c1b4fe2a0c7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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