spc-simulator/ledger.html
2026-07-18 09:28:25 +00:00

591 lines
18 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Control Ledger | Brian Beaulieu</title>
<link rel="stylesheet" href="style.css">
<script defer src="https://analytics.4ort.xyz/script.js" data-website-id="d3ed927c-888a-4a6c-ae5f-0b1c613ddf5b"></script>
</head>
<body>
<header class="ledger-hero">
<div class="overlay"></div>
<div class="hero-content">
<nav class="breadcrumb">
<a href="index.html">← Home</a>
</nav>
<h1>Control Ledger<br><span class="highlight">Real-Time Process Monitoring</span></h1>
<p class="lead">When variance exceeds tolerance, the ledger triggers response. Built on Statistical Process Control principles.</p>
</div>
</header>
<main class="ledger-container">
<div class="interactive-demo">
<div class="demo-panel">
<h2>Live Simulation</h2>
<p>Adjust process parameters and watch control limits recalculate.</p>
<div class="controls">
<div class="slider-group">
<label for="mean-input">Process Mean (μ)</label>
<input type="range" id="mean-input" min="85" max="115" value="100" step="0.5">
<output id="mean-output">100.0</output>
</div>
<div class="slider-group">
<label for="sigma-input">Standard Deviation (σ)</label>
<input type="range" id="sigma-input" min="0.5" max="8" value="2.5" step="0.1">
<output id="sigma-output">2.5</output>
</div>
<div class="slider-group">
<label for="sample-input">Sample Size (n)</label>
<input type="range" id="sample-input" min="2" max="25" value="5" step="1">
<output id="sample-output">5</output>
</div>
</div>
<div class="chart-display">
<canvas id="controlChart" width="800" height="400"></canvas>
<div class="chart-overlay">
<div class="limit-label ucl">UCL = <span id="ucl-value">103.77</span></div>
<div class="limit-label cl">Center Line = <span id="cl-value">100.00</span></div>
<div class="limit-label lcl">LCL = <span id="lcl-value">96.23</span></div>
</div>
</div>
<div class="status-indicator">
<span class="status-dot" id="status-dot"></span>
<span id="status-text">PROCESS IN CONTROL</span>
</div>
</div>
<div class="theory-panel">
<h2>X-bar Chart Mathematics</h2>
<div class="formula-block">
<code>UCL = μ̄ + A₂ ×</code>
<p class="annotation">Upper Control Limit</p>
</div>
<div class="formula-block">
<code>LCL = μ̄ A₂ ×</code>
<p class="annotation">Lower Control Limit</p>
</div>
<div class="formula-block">
<code>A₂ = 3 / (d₂ × √n)</code>
<p class="annotation">Control chart constant (from d₂ table)</p>
</div>
<div class="insight-box">
<h3>Why This Matters</h3>
<p>In galactic logistics, a 3-sigma excursion isn't noise—it's a supply lane fracture waiting to happen. The ledger doesn't predict; it detects early enough to respond.</p>
</div>
</div>
</div>
<section class="implementation-guide">
<div class="container">
<h2>Deployment Protocol</h2>
<div class="steps-grid">
<div class="step-card">
<span class="step-num">01</span>
<h3>Select Critical Parameters</h3>
<p>Identify CTQs (Critical-to-Quality metrics) where variance directly impacts mission success.</p>
</div>
<div class="step-card">
<span class="step-num">02</span>
<h3>Establish Baseline</h3>
<p>Collect Phase 1 data under stable conditions. Calculate μ₀ and σ₀.</p>
</div>
<div class="step-card">
<span class="step-num">03</span>
<h3>Set Response Triggers</h3>
<p>Define escalation paths for 2-sigma warnings and 3-sigma alarms.</p>
</div>
<div class="step-card">
<span class="step-num">04</span>
<h3>Automate Monitoring</h3>
<p>Deploy sensor network feeding real-time X-bar calculations to dashboard.</p>
</div>
</div>
</div>
</section>
</main>
<footer>
<p>&copy; 2026 Brian Beaulieu | Control Ledger v1.0 — Interactive SPC Module</p>
</footer>
</body>
</html>
<style>
/* LEDGER-SPECIFIC STYLES */
.ledger-hero {
position: relative;
height: 50vh;
display: flex;
align-items: center;
overflow: hidden;
background: radial-gradient(circle at 70% 30%, #1a1a2e 0%, #0a0a0c 80%);
}
.ledger-hero .overlay {
background: linear-gradient(180deg, rgba(10,10,12,0.95) 0%, rgba(10,10,12,0.8) 100%);
}
.ledger-hero .hero-content {
max-width: 650px;
padding: 0 2rem;
}
.ledger-hero h1 {
font-size: clamp(2rem, 4vw, 3rem);
line-height: 1.1;
margin-bottom: 1rem;
}
.ledger-container {
padding: 3rem 2rem;
background: var(--bg-deep);
}
.interactive-demo {
max-width: 1200px;
margin: 0 auto;
display: grid;
grid-template-columns: 1.2fr 0.8fr;
gap: 2rem;
}
@media (max-width: 1024px) {
.interactive-demo {
grid-template-columns: 1fr;
}
}
.demo-panel {
background: var(--bg-card);
border: 1px solid var(--border-subtle);
border-radius: 16px;
padding: 2rem;
}
.demo-panel h2 {
font-size: 1.5rem;
margin-bottom: 0.5rem;
color: var(--accent-blue);
}
.demo-panel > p {
color: var(--text-muted);
margin-bottom: 2rem;
font-size: 0.95rem;
}
.controls {
background: rgba(0,0,0,0.3);
border-radius: 12px;
padding: 1.5rem;
margin-bottom: 2rem;
}
.slider-group {
display: grid;
grid-template-columns: 1fr auto;
align-items: center;
gap: 1.5rem;
margin-bottom: 1.25rem;
}
.slider-group:last-child {
margin-bottom: 0;
}
.slider-group label {
font-size: 0.85rem;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.08em;
}
.slider-group input[type="range"] {
grid-column: 1 / -1;
width: 100%;
margin-bottom: 0.5rem;
accent-color: var(--accent-blue);
height: 4px;
background: var(--border-subtle);
border-radius: 2px;
appearance: none;
}
.slider-group output {
font-family: "SF Mono", "Menlo", monospace;
font-size: 0.9rem;
color: var(--accent-blue);
font-weight: 600;
}
.chart-display {
position: relative;
margin-bottom: 1.5rem;
border: 1px solid var(--border-subtle);
border-radius: 8px;
overflow: hidden;
}
.chart-display canvas {
display: block;
width: 100%;
height: auto;
background: linear-gradient(180deg, #0d0d12 0%, #0a0a0c 100%);
}
.chart-overlay {
position: absolute;
top: 1rem;
right: 1rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
align-items: flex-end;
}
.limit-label {
font-family: "SF Mono", "Menlo", monospace;
font-size: 0.75rem;
padding: 0.25rem 0.5rem;
border-radius: 4px;
backdrop-filter: blur(8px);
}
.limit-label.ucl {
color: #ef4444;
background: rgba(239,68,68,0.1);
border: 1px solid rgba(239,68,68,0.3);
}
.limit-label.cl {
color: var(--accent-blue);
background: rgba(59,130,246,0.1);
border: 1px solid rgba(59,130,246,0.3);
}
.limit-label.lcl {
color: #22c55e;
background: rgba(34,197,94,0.1);
border: 1px solid rgba(34,197,94,0.3);
}
.status-indicator {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 1rem;
background: rgba(34,197,94,0.08);
border: 1px solid rgba(34,197,94,0.2);
border-radius: 8px;
}
.status-dot {
width: 12px;
height: 12px;
border-radius: 50%;
background: #22c55e;
box-shadow: 0 0 12px rgba(34,197,94,0.6);
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.7; transform: scale(1.1); }
}
.status-text {
font-family: "SF Mono", "Menlo", monospace;
font-size: 0.8rem;
color: #22c55e;
text-transform: uppercase;
letter-spacing: 0.12em;
font-weight: 600;
}
.theory-panel {
background: var(--bg-card);
border: 1px solid var(--border-subtle);
border-radius: 16px;
padding: 2rem;
}
.theory-panel h2 {
font-size: 1.25rem;
margin-bottom: 1.5rem;
color: var(--accent-blue);
}
.formula-block {
background: rgba(59,130,246,0.05);
border-left: 3px solid var(--accent-blue);
padding: 1rem;
margin-bottom: 1rem;
border-radius: 0 8px 8px 0;
}
.formula-block code {
display: block;
font-family: "SF Mono", "Menlo", monospace;
font-size: 0.9rem;
color: var(--text-primary);
margin-bottom: 0.5rem;
}
.formula-block .annotation {
font-size: 0.75rem;
color: var(--text-muted);
font-style: italic;
}
.insight-box {
margin-top: 2rem;
padding: 1.5rem;
background: linear-gradient(180deg, rgba(245,158,11,0.08) 0%, transparent 100%);
border: 1px solid rgba(245,158,11,0.2);
border-radius: 12px;
}
.insight-box h3 {
font-size: 1rem;
color: var(--accent-warm);
margin-bottom: 0.75rem;
}
.insight-box p {
font-size: 0.9rem;
color: var(--text-muted);
line-height: 1.7;
}
.implementation-guide {
max-width: 1000px;
margin: 4rem auto 0;
}
.implementation-guide h2 {
text-align: center;
font-size: 2rem;
margin-bottom: 3rem;
color: var(--accent-blue);
}
.steps-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 1.5rem;
}
.step-card {
background: var(--bg-card);
border: 1px solid var(--border-subtle);
border-radius: 12px;
padding: 1.5rem;
position: relative;
overflow: hidden;
}
.step-card::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 4px;
height: 100%;
background: var(--accent-blue);
opacity: 0.5;
}
.step-num {
position: absolute;
top: 1rem;
right: 1rem;
font-size: 3rem;
font-weight: 700;
color: rgba(255,255,255,0.03);
font-family: "SF Mono", "Menlo", monospace;
}
.step-card h3 {
font-size: 1.1rem;
margin-bottom: 0.75rem;
color: var(--accent-blue);
}
.step-card p {
font-size: 0.9rem;
color: var(--text-muted);
line-height: 1.6;
}
footer {
margin-top: 4rem;
}
</style>
<script>
// CONTROL CHART SIMULATION
const canvas = document.getElementById('controlChart');
const ctx = canvas.getContext('2d');
const meanInput = document.getElementById('mean-input');
const sigmaInput = document.getElementById('sigma-input');
const sampleInput = document.getElementById('sample-input');
const meanOutput = document.getElementById('mean-output');
const sigmaOutput = document.getElementById('sigma-output');
const sampleOutput = document.getElementById('sample-output');
const uclValue = document.getElementById('ucl-value');
const clValue = document.getElementById('cl-value');
const lclValue = document.getElementById('lcl-value');
const statusDot = document.getElementById('status-dot');
const statusText = document.getElementById('status-text');
let samples = [];
const NUM_SAMPLES = 50;
function updateDisplay() {
const mu = parseFloat(meanInput.value);
const sigma = parseFloat(sigmaInput.value);
const n = parseInt(sampleInput.value);
meanOutput.textContent = mu.toFixed(1);
sigmaOutput.textContent = sigma.toFixed(1);
sampleOutput.textContent = n.toString();
// Control chart constants (approximation for A2)
const d2_table = {2:1.128, 3:1.693, 4:2.059, 5:2.326, 6:2.534, 7:2.704, 8:2.847, 9:2.970, 10:3.078};
const d2 = d2_table[n] || (2.326 + (n-5)*0.15);
const A2 = 3 / (d2 * Math.sqrt(n));
const UCL = mu + A2 * sigma * d2;
const LCL = mu - A2 * sigma * d2;
uclValue.textContent = UCL.toFixed(2);
clValue.textContent = mu.toFixed(2);
lclValue.textContent = LCL.toFixed(2);
drawChart(mu, sigma, UCL, LCL, n);
}
function generateSamples(mu, sigma) {
samples = [];
for (let i = 0; i < NUM_SAMPLES; i++) {
// Box-Muller transform for normal distribution
let u = 0, v = 0;
while(u === 0) u = Math.random();
while(v === 0) v = Math.random();
const z = Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v);
samples.push(mu + z * sigma);
}
}
function drawChart(mu, sigma, UCL, LCL, n) {
const width = canvas.width;
const height = canvas.height;
const padding = {top: 40, right: 60, bottom: 40, left: 60};
const plotWidth = width - padding.left - padding.right;
const plotHeight = height - padding.top - padding.bottom;
// Clear
ctx.fillStyle = '#0d0d12';
ctx.fillRect(0, 0, width, height);
// Generate new samples
generateSamples(mu, sigma);
// Find range
const minVal = Math.min(...samples, LCL - sigma);
const maxVal = Math.max(...samples, UCL + sigma);
const valRange = maxVal - minVal;
// Scale function
const scaleX = (i) => padding.left + (i / (NUM_SAMPLES - 1)) * plotWidth;
const scaleY = (val) => padding.top + ((val - minVal) / valRange) * plotHeight;
// Draw control limits
ctx.strokeStyle = '#ef4444';
ctx.setLineDash([8, 4]);
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(padding.left, scaleY(UCL));
ctx.lineTo(padding.left + plotWidth, scaleY(UCL));
ctx.stroke();
ctx.strokeStyle = '#3b82f6';
ctx.setLineDash([]);
ctx.beginPath();
ctx.moveTo(padding.left, scaleY(mu));
ctx.lineTo(padding.left + plotWidth, scaleY(mu));
ctx.stroke();
ctx.strokeStyle = '#22c55e';
ctx.setLineDash([8, 4]);
ctx.beginPath();
ctx.moveTo(padding.left, scaleY(LCL));
ctx.lineTo(padding.left + plotWidth, scaleY(LCL));
ctx.stroke();
// Draw data points
ctx.fillStyle = '#3b82f6';
samples.forEach((val, i) => {
const x = scaleX(i);
const y = scaleY(val);
ctx.beginPath();
ctx.arc(x, y, 4, 0, Math.PI * 2);
ctx.fill();
// Check out of control
if (val > UCL || val < LCL) {
ctx.fillStyle = '#ef4444';
ctx.beginPath();
ctx.arc(x, y, 6, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#3b82f6';
// Update status
statusDot.style.background = '#ef4444';
statusDot.style.boxShadow = '0 0 12px rgba(239,68,68,0.6)';
statusText.textContent = 'OUT OF CONTROL DETECTED';
statusText.style.color = '#ef4444';
}
});
// Reset status if all in control
const allInControl = samples.every(s => s >= LCL && s <= UCL);
if (allInControl) {
statusDot.style.background = '#22c55e';
statusDot.style.boxShadow = '0 0 12px rgba(34,197,94,0.6)';
statusText.textContent = 'PROCESS IN CONTROL';
statusText.style.color = '#22c55e';
}
// Axis labels
ctx.fillStyle = '#9a9a9a';
ctx.font = '10px SF Mono, Menlo, monospace';
ctx.textAlign = 'left';
ctx.fillText(`UCL=${UCL.toFixed(1)}`, padding.left + 8, padding.top - 8);
ctx.fillText(`CL=${mu.toFixed(1)}`, padding.left + 8, scaleY(mu) + 4);
ctx.fillText(`LCL=${LCL.toFixed(1)}`, padding.left + 8, scaleY(LCL) + 4);
}
// Event listeners
meanInput.addEventListener('input', updateDisplay);
sigmaInput.addEventListener('input', updateDisplay);
sampleInput.addEventListener('input', updateDisplay);
// Initial render
updateDisplay();
// Handle resize
window.addEventListener('resize', () => {
canvas.width = canvas.parentElement.clientWidth;
canvas.height = 400;
updateDisplay();
});
</script>