Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Collaborative Holographic Universe Simulation</title> | |
<style> | |
body { | |
font-family: 'Arial', sans-serif; | |
background: linear-gradient(135deg, #000, #111); | |
margin: 0; | |
padding: 0; | |
color: #ccf; | |
min-height: 100vh; | |
display: flex; | |
flex-direction: column; | |
justify-content: center; | |
align-items: center; | |
} | |
.container { | |
width: 90%; | |
max-width: 1200px; | |
margin: 0 auto; | |
padding: 20px; | |
} | |
.header { | |
text-align: center; | |
margin-bottom: 20px; | |
} | |
h1 { | |
font-size: 2.5rem; | |
color: #7cf; | |
text-shadow: 0 0 10px rgba(124, 255, 255, 0.7); | |
} | |
.quantum-simulator { | |
background: rgba(0, 0, 15, 0.7); | |
border-radius: 15px; | |
padding: 20px; | |
margin-bottom: 30px; | |
box-shadow: 0 0 20px rgba(124, 200, 255, 0.3); | |
} | |
.env-setup { | |
background: rgba(15, 0, 30, 0.7); | |
border-radius: 15px; | |
padding: 20px; | |
margin-bottom: 30px; | |
box-shadow: 0 0 20px rgba(180, 120, 255, 0.3); | |
} | |
.analysis { | |
background: rgba(0, 30, 15, 0.7); | |
border-radius: 15px; | |
padding: 20px; | |
box-shadow: 0 0 20px rgba(120, 255, 170, 0.3); | |
} | |
.section-title { | |
color: #adf; | |
font-size: 1.6rem; | |
margin-bottom: 15px; | |
} | |
.row { | |
display: flex; | |
flex-wrap: wrap; | |
margin: 0 -10px; | |
} | |
.column { | |
flex: 1 1 300px; | |
margin: 10px; | |
} | |
canvas { | |
width: 100%; | |
height: auto; | |
background: #000; | |
border-radius: 10px; | |
max-height: 300px; | |
} | |
button { | |
background: linear-gradient(45deg, #4a7bd1, #7a4ad1); | |
color: white; | |
border: none; | |
padding: 12px 20px; | |
border-radius: 5px; | |
font-size: 16px; | |
cursor: pointer; | |
margin-top: 10px; | |
transition: all 0.3s; | |
} | |
button:hover { | |
transform: scale(1.05); | |
box-shadow: 0 0 15px rgba(124, 200, 255, 0.6); | |
} | |
.control-panel { | |
display: flex; | |
flex-wrap: wrap; | |
gap: 10px; | |
margin-bottom: 15px; | |
} | |
.parameter-control { | |
display: flex; | |
flex-direction: column; | |
min-width: 120px; | |
} | |
label { | |
margin-bottom: 5px; | |
color: #8cf; | |
} | |
input { | |
padding: 8px; | |
border-radius: 4px; | |
border: 1px solid #446; | |
background: #112; | |
color: #ddf; | |
} | |
.code-block { | |
background: #001; | |
padding: 15px; | |
border-radius: 5px; | |
font-family: monospace; | |
overflow-x: auto; | |
color: #8df; | |
margin-top: 10px; | |
} | |
.visualization { | |
width: 100%; | |
height: 280px; | |
margin-top: 15px; | |
position: relative; | |
} | |
.visualizer-canvas { | |
background: linear-gradient(to bottom, #000510, #001030); | |
border: 1px solid #333366; | |
width: 100%; | |
height: 100%; | |
border-radius: 8px; | |
} | |
.metrics { | |
display: grid; | |
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); | |
gap: 15px; | |
margin-top: 15px; | |
} | |
.metric-card { | |
background: rgba(20, 30, 50, 0.6); | |
padding: 15px; | |
border-radius: 8px; | |
text-align: center; | |
} | |
.metric-value { | |
font-size: 1.8rem; | |
color: #7df; | |
margin: 10px 0; | |
font-weight: bold; | |
} | |
.metric-label { | |
color: #adf; | |
font-size: 0.9rem; | |
} | |
@media (max-width: 768px) { | |
.column { | |
flex: 1 1 100%; | |
} | |
} | |
.user-avatar { | |
width: 30px; | |
height: 30px; | |
border-radius: 50%; | |
margin-right: 5px; | |
} | |
.users-list { | |
background: rgba(10, 20, 40, 0.7); | |
border-radius: 10px; | |
padding: 15px; | |
margin-bottom: 20px; | |
max-height: 150px; | |
overflow-y: auto; | |
} | |
.user-container { | |
display: flex; | |
align-items: center; | |
margin-bottom: 8px; | |
color: #adf; | |
font-size: 0.9rem; | |
} | |
.user-cursor { | |
position: absolute; | |
pointer-events: none; | |
z-index: 1000; | |
font-size: 12px; | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
} | |
.cursor-pointer { | |
width: 20px; | |
height: 20px; | |
border-radius: 50%; | |
} | |
.cursor-label { | |
background: rgba(0, 0, 0, 0.7); | |
color: white; | |
padding: 2px 5px; | |
border-radius: 3px; | |
margin-top: 5px; | |
white-space: nowrap; | |
} | |
.chat-container { | |
background: rgba(10, 20, 40, 0.7); | |
border-radius: 10px; | |
padding: 15px; | |
margin-top: 20px; | |
display: flex; | |
flex-direction: column; | |
height: 200px; | |
} | |
.chat-messages { | |
flex-grow: 1; | |
overflow-y: auto; | |
margin-bottom: 10px; | |
color: #ddf; | |
} | |
.chat-input-container { | |
display: flex; | |
} | |
.chat-input { | |
flex-grow: 1; | |
padding: 8px; | |
border-radius: 4px; | |
border: 1px solid #446; | |
background: #112; | |
color: #ddf; | |
margin-right: 5px; | |
} | |
.chat-send { | |
background: linear-gradient(45deg, #4a7bd1, #7a4ad1); | |
color: white; | |
border: none; | |
padding: 8px 15px; | |
border-radius: 5px; | |
cursor: pointer; | |
} | |
.message { | |
margin-bottom: 8px; | |
} | |
.message-user { | |
font-weight: bold; | |
color: #7cf; | |
} | |
.message-text { | |
color: #ccf; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<div class="header"> | |
<h1>Collaborative Holographic Universe Simulation</h1> | |
<p>Explore quantum circuits and holographic frameworks together in real-time</p> | |
</div> | |
<div class="users-list" id="users-list"> | |
<h3>Connected Users</h3> | |
<div id="users-container"></div> | |
</div> | |
<div class="quantum-simulator"> | |
<h2 class="section-title">Quantum Circuit Setup</h2> | |
<div class="row"> | |
<div class="column"> | |
<div class="control-panel"> | |
<div class="parameter-control"> | |
<label for="num-qubits">Number of Qubits</label> | |
<input type="number" id="num-qubits" min="1" max="8" value="3"> | |
</div> | |
<div class="parameter-control"> | |
<label for="num-shots">Number of Shots</label> | |
<input type="number" id="num-shots" min="100" max="10000" value="1024"> | |
</div> | |
</div> | |
<div class="code-block"> | |
<pre> | |
# Define a quantum register and circuit | |
qr = QuantumRegister(3, 'q') | |
cr = ClassicalRegister(3, 'c') | |
qc = QuantumCircuit(qr, cr) | |
# Apply Hadamard gates to create superposition | |
for i in range(qr.size()): | |
qc.h(qr[i]) | |
# Apply CNOT gate to entangle qubits | |
qc.cx(qr[0], qr[1]) | |
# Measure all qubits | |
qc.measure(qr, cr)</pre> | |
</div> | |
<button id="run-circuit">Run Quantum Circuit</button> | |
</div> | |
<div class="column"> | |
<div class="visualization"> | |
<canvas id="circuit-canvas" class="visualizer-canvas"></canvas> | |
</div> | |
<div class="metrics"> | |
<div class="metric-card"> | |
<div class="metric-label">Entanglement Measure</div> | |
<div class="metric-value" id="entanglement-value">0.82</div> | |
</div> | |
<div class="metric-card"> | |
<div class="metric-label">Coherence</div> | |
<div class="metric-value" id="coherence-value">0.65</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="env-setup"> | |
<h2 class="section-title">Holographic Environment Setup</h2> | |
<div class="row"> | |
<div class="column"> | |
<div class="control-panel"> | |
<div class="parameter-control"> | |
<label for="time-const">Time Constant (t)</label> | |
<input type="number" id="time-const" min="0" max="3" value="1"> | |
</div> | |
<div class="parameter-control"> | |
<label for="space-scale">Space Scale (s)</label> | |
<input type="number" id="space-scale" min="0" max="3" value="2"> | |
</div> | |
<div class="parameter-control"> | |
<label for="mass-value">Mass Value (m)</label> | |
<input type="number" id="mass-value" min="0" max="3" value="3"> | |
</div> | |
<div class="parameter-control"> | |
<label for="dimension-num">Dimensions (d)</label> | |
<input type="number" id="dimension-num" min="0" max="3" value="1"> | |
</div> | |
</div> | |
<div class="code-block"> | |
<pre> | |
# Define environment variables (2-bit representations) | |
t = 0b01 # Planck time (scaled) | |
s = 0b10 # Scale of space | |
m = 0b11 # Simplified number of atoms | |
d = 0b01 # Number of dimensions | |
# Concatenate to form 8-bit environment label | |
env_label = f"{t:02b}{s:02b}{m:02b}{d:02b}" | |
# Create environment state vector | |
env_state = Statevector.from_label(env_label)</pre> | |
</div> | |
<button id="update-env">Update Environment</button> | |
</div> | |
<div class="column"> | |
<div class="visualization"> | |
<canvas id="environment-canvas" class="visualizer-canvas"></canvas> | |
</div> | |
<div class="metrics"> | |
<div class="metric-card"> | |
<div class="metric-label">Environment Label</div> | |
<div class="metric-value" id="env-label-value">01101101</div> | |
</div> | |
<div class="metric-card"> | |
<div class="metric-label">State Complexity</div> | |
<div class="metric-value" id="complexity-value">3.14</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="analysis"> | |
<h2 class="section-title">Analysis and Results</h2> | |
<div class="row"> | |
<div class="column"> | |
<div class="visualization"> | |
<canvas id="histogram-canvas" class="visualizer-canvas"></canvas> | |
</div> | |
<div class="metrics"> | |
<div class="metric-card"> | |
<div class="metric-label">Mean Count</div> | |
<div class="metric-value" id="mean-count">128.00</div> | |
</div> | |
<div class="metric-card"> | |
<div class="metric-label">Variance</div> | |
<div class="metric-value" id="variance-count">42.56</div> | |
</div> | |
</div> | |
</div> | |
<div class="column"> | |
<div class="visualization"> | |
<canvas id="evolution-canvas" class="visualizer-canvas"></canvas> | |
</div> | |
<div class="metrics"> | |
<div class="metric-card"> | |
<div class="metric-label">State Fidelity</div> | |
<div class="metric-value" id="fidelity-value">0.78</div> | |
</div> | |
<div class="metric-card"> | |
<div class="metric-label">Quantum Entropy</div> | |
<div class="metric-value" id="entropy-value">1.23</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<button id="run-analysis">Run Full Analysis</button> | |
</div> | |
<div class="chat-container"> | |
<div class="chat-messages" id="chat-messages"></div> | |
<div class="chat-input-container"> | |
<input type="text" class="chat-input" id="chat-input" placeholder="Type your message..."> | |
<button class="chat-send" id="chat-send">Send</button> | |
</div> | |
</div> | |
</div> | |
<script> | |
// Initialize WebSocket for multiplayer functionality | |
const room = new WebsimSocket(); | |
let clientId, username; | |
const cursors = {}; | |
// Map to track user colors | |
const userColors = {}; | |
// Track when user last moved cursor (to avoid too many updates) | |
let lastCursorUpdate = 0; | |
// Initialize canvases | |
const canvases = { | |
circuit: document.getElementById('circuit-canvas'), | |
environment: document.getElementById('environment-canvas'), | |
histogram: document.getElementById('histogram-canvas'), | |
evolution: document.getElementById('evolution-canvas') | |
}; | |
// Initialize contexts | |
const contexts = {}; | |
for (const [key, canvas] of Object.entries(canvases)) { | |
contexts[key] = canvas.getContext('2d'); | |
canvas.width = canvas.clientWidth; | |
canvas.height = canvas.clientHeight; | |
} | |
// Simulation state | |
const state = { | |
qubits: 3, | |
shots: 1024, | |
environmentParams: { | |
t: 1, // Time constant | |
s: 2, // Space scale | |
m: 3, // Mass value | |
d: 1 // Dimensions | |
}, | |
measurements: {}, | |
environmentState: {}, | |
statistics: { | |
mean: 128.0, | |
variance: 42.56, | |
fidelity: 0.78, | |
entropy: 1.23, | |
entanglement: 0.82, | |
coherence: 0.65, | |
complexity: 3.14 | |
} | |
}; | |
// Draw quantum circuit visualization | |
function drawCircuit() { | |
const ctx = contexts.circuit; | |
const width = canvases.circuit.width; | |
const height = canvases.circuit.height; | |
ctx.clearRect(0, 0, width, height); | |
// Draw background grid | |
ctx.strokeStyle = 'rgba(100, 150, 255, 0.1)'; | |
ctx.lineWidth = 1; | |
const gridSize = 20; | |
for (let x = 0; x <= width; x += gridSize) { | |
ctx.beginPath(); | |
ctx.moveTo(x, 0); | |
ctx.lineTo(x, height); | |
ctx.stroke(); | |
} | |
for (let y = 0; y <= height; y += gridSize) { | |
ctx.beginPath(); | |
ctx.moveTo(0, y); | |
ctx.lineTo(width, y); | |
ctx.stroke(); | |
} | |
// Draw qubit lines | |
const qubitSpacing = height / (state.qubits + 1); | |
ctx.strokeStyle = 'rgba(100, 200, 255, 0.8)'; | |
ctx.lineWidth = 2; | |
for (let i = 0; i < state.qubits; i++) { | |
const y = (i + 1) * qubitSpacing; | |
ctx.beginPath(); | |
ctx.moveTo(50, y); | |
ctx.lineTo(width - 50, y); | |
ctx.stroke(); | |
// Qubit label | |
ctx.fillStyle = 'rgba(150, 220, 255, 0.8)'; | |
ctx.font = '12px Arial'; | |
ctx.textAlign = 'right'; | |
ctx.fillText(`q${i}`, 40, y + 4); | |
} | |
// Draw Hadamard gates | |
const gateWidth = 30; | |
const gateHeight = 30; | |
const hGateX = 100; | |
for (let i = 0; i < state.qubits; i++) { | |
const y = (i + 1) * qubitSpacing - gateHeight / 2; | |
ctx.fillStyle = 'rgba(100, 150, 255, 0.7)'; | |
ctx.fillRect(hGateX, y, gateWidth, gateHeight); | |
ctx.fillStyle = 'white'; | |
ctx.font = 'bold 16px Arial'; | |
ctx.textAlign = 'center'; | |
ctx.textBaseline = 'middle'; | |
ctx.fillText('H', hGateX + gateWidth / 2, y + gateHeight / 2); | |
} | |
// Draw CNOT gate | |
const cnotX = 200; | |
const controlY = 1 * qubitSpacing; | |
const targetY = 2 * qubitSpacing; | |
// Control point | |
ctx.fillStyle = 'rgba(255, 100, 100, 0.8)'; | |
ctx.beginPath(); | |
ctx.arc(cnotX, controlY, 5, 0, Math.PI * 2); | |
ctx.fill(); | |
// Line connecting control and target | |
ctx.strokeStyle = 'rgba(255, 100, 100, 0.8)'; | |
ctx.beginPath(); | |
ctx.moveTo(cnotX, controlY); | |
ctx.lineTo(cnotX, targetY); | |
ctx.stroke(); | |
// Target circle | |
ctx.beginPath(); | |
ctx.arc(cnotX, targetY, 15, 0, Math.PI * 2); | |
ctx.stroke(); | |
// Plus symbol | |
ctx.beginPath(); | |
ctx.moveTo(cnotX - 10, targetY); | |
ctx.lineTo(cnotX + 10, targetY); | |
ctx.moveTo(cnotX, targetY - 10); | |
ctx.lineTo(cnotX, targetY + 10); | |
ctx.stroke(); | |
// Draw measurement operations | |
const measureX = 300; | |
for (let i = 0; i < state.qubits; i++) { | |
const y = (i + 1) * qubitSpacing; | |
// Measurement symbol | |
ctx.strokeStyle = 'rgba(150, 255, 150, 0.8)'; | |
ctx.fillStyle = 'rgba(100, 200, 100, 0.3)'; | |
// Box | |
ctx.fillRect(measureX, y - 15, 40, 30); | |
ctx.strokeRect(measureX, y - 15, 40, 30); | |
// Meter symbol | |
ctx.beginPath(); | |
ctx.moveTo(measureX + 10, y - 5); | |
ctx.lineTo(measureX + 10, y + 5); | |
ctx.lineTo(measureX + 30, y - 5); | |
ctx.lineTo(measureX + 30, y + 5); | |
ctx.stroke(); | |
} | |
} | |
// Draw holographic environment visualization | |
function drawEnvironment() { | |
const ctx = contexts.environment; | |
const width = canvases.environment.width; | |
const height = canvases.environment.height; | |
ctx.clearRect(0, 0, width, height); | |
// Draw background | |
const gradient = ctx.createLinearGradient(0, 0, width, height); | |
gradient.addColorStop(0, 'rgba(0, 10, 30, 0.5)'); | |
gradient.addColorStop(1, 'rgba(30, 0, 60, 0.5)'); | |
ctx.fillStyle = gradient; | |
ctx.fillRect(0, 0, width, height); | |
// Update environment label display | |
const envLabel = `${state.environmentParams.t.toString(2).padStart(2, '0')}${state.environmentParams.s.toString(2).padStart(2, '0')}${state.environmentParams.m.toString(2).padStart(2, '0')}${state.environmentParams.d.toString(2).padStart(2, '0')}`; | |
document.getElementById('env-label-value').textContent = envLabel; | |
// Draw bit representations | |
const bitSize = 30; | |
const startX = width / 2 - (8 * bitSize) / 2; | |
const y = height / 2; | |
for (let i = 0; i < 8; i++) { | |
const x = startX + i * bitSize; | |
const bitValue = envLabel[i]; | |
ctx.fillStyle = bitValue === '1' ? 'rgba(100, 200, 255, 0.8)' : 'rgba(50, 50, 100, 0.5)'; | |
ctx.fillRect(x, y - bitSize / 2, bitSize - 2, bitSize); | |
ctx.fillStyle = 'white'; | |
ctx.font = '14px Arial'; | |
ctx.textAlign = 'center'; | |
ctx.textBaseline = 'middle'; | |
ctx.fillText(bitValue, x + bitSize / 2 - 1, y); | |
} | |
// Draw parameter indicators | |
const paramRadius = 50; | |
const centerX = width / 2; | |
const centerY = height / 2 - 70; | |
// Draw parameter circles | |
const params = [ | |
{ name: 't', value: state.environmentParams.t, color: 'rgba(255, 100, 100, 0.7)' }, | |
{ name: 's', value: state.environmentParams.s, color: 'rgba(100, 255, 100, 0.7)' }, | |
{ name: 'm', value: state.environmentParams.m, color: 'rgba(100, 100, 255, 0.7)' }, | |
{ name: 'd', value: state.environmentParams.d, color: 'rgba(255, 255, 100, 0.7)' } | |
]; | |
for (let i = 0; i < params.length; i++) { | |
const angle = (i * Math.PI / 2) - Math.PI / 4; | |
const x = centerX + Math.cos(angle) * paramRadius; | |
const y = centerY + Math.sin(angle) * paramRadius; | |
const param = params[i]; | |
// Circle | |
ctx.fillStyle = param.color; | |
ctx.beginPath(); | |
ctx.arc(x, y, 20, 0, Math.PI * 2); | |
ctx.fill(); | |
// Label | |
ctx.fillStyle = 'white'; | |
ctx.font = 'bold 16px Arial'; | |
ctx.textAlign = 'center'; | |
ctx.textBaseline = 'middle'; | |
ctx.fillText(param.name, x, y - 2); | |
// Value | |
ctx.font = '12px Arial'; | |
ctx.fillText(param.value, x, y + 12); | |
} | |
// Draw connecting lines | |
ctx.strokeStyle = 'rgba(150, 150, 255, 0.4)'; | |
ctx.lineWidth = 1; | |
for (let i = 0; i < params.length; i++) { | |
const angle1 = (i * Math.PI / 2) - Math.PI / 4; | |
const x1 = centerX + Math.cos(angle1) * paramRadius; | |
const y1 = centerY + Math.sin(angle1) * paramRadius; | |
for (let j = i + 1; j < params.length; j++) { | |
const angle2 = (j * Math.PI / 2) - Math.PI / 4; | |
const x2 = centerX + Math.cos(angle2) * paramRadius; | |
const y2 = centerY + Math.sin(angle2) * paramRadius; | |
ctx.beginPath(); | |
ctx.moveTo(x1, y1); | |
ctx.lineTo(x2, y2); | |
ctx.stroke(); | |
} | |
} | |
// Draw holographic effect | |
for (let i = 0; i < 50; i++) { | |
const angle = Math.random() * Math.PI * 2; | |
const distance = Math.random() * 100 + 30; | |
const x = centerX + Math.cos(angle) * distance; | |
const y = centerY + Math.sin(angle) * distance; | |
ctx.fillStyle = `rgba(150, 220, 255, ${Math.random() * 0.2})`; | |
ctx.beginPath(); | |
ctx.arc(x, y, Math.random() * 5 + 1, 0, Math.PI * 2); | |
ctx.fill(); | |
} | |
// Update complexity metric | |
const complexity = (1 + Math.sin(Date.now() / 1000) * 0.1) * 3.14; | |
document.getElementById('complexity-value').textContent = complexity.toFixed(2); | |
} | |
// Draw measurement histogram | |
function drawHistogram() { | |
const ctx = contexts.histogram; | |
const width = canvases.histogram.width; | |
const height = canvases.histogram.height; | |
ctx.clearRect(0, 0, width, height); | |
// Generate some sample measurement data based on current qubits | |
const possibleOutcomes = Math.pow(2, state.qubits); | |
const measurements = {}; | |
let total = 0; | |
// Create plausible quantum distribution | |
for (let i = 0; i < possibleOutcomes; i++) { | |
const bitString = i.toString(2).padStart(state.qubits, '0'); | |
// Generate values with a slight bias towards balanced states | |
let probability = Math.random(); | |
// Add interference pattern for more realistic quantum results | |
const hammingWeight = bitString.split('').filter(bit => bit === '1').length; | |
const balance = Math.abs(hammingWeight - state.qubits / 2) / state.qubits; | |
probability = probability * (1 - balance * 0.5); | |
// Add entanglement effect (correlations between bits 0 and 1) | |
if (state.qubits >= 2 && bitString[0] === bitString[1]) { | |
probability *= 1.5; | |
} | |
const count = Math.floor(probability * state.shots / possibleOutcomes * 2); | |
measurements[bitString] = count; | |
total += count; | |
} | |
// Normalize to ensure total equals shots | |
for (const outcome in measurements) { | |
measurements[outcome] = Math.floor(measurements[outcome] * state.shots / total); | |
} | |
// Calculate statistics | |
const values = Object.values(measurements); | |
const mean = values.reduce((sum, val) => sum + val, 0) / values.length; | |
const variance = values.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / values.length; | |
state.statistics.mean = mean; | |
state.statistics.variance = variance; | |
// Update metrics display | |
document.getElementById('mean-count').textContent = mean.toFixed(2); | |
document.getElementById('variance-count').textContent = variance.toFixed(2); | |
// Draw histogram | |
const outcomes = Object.keys(measurements).sort(); | |
const barWidth = width / outcomes.length; | |
const maxHeight = height - 40; | |
const maxCount = Math.max(...Object.values(measurements)); | |
// Draw bars | |
for (let i = 0; i < outcomes.length; i++) { | |
const outcome = outcomes[i]; | |
const count = measurements[outcome]; | |
const barHeight = (count / maxCount) * maxHeight; | |
const hue = 240 - (i / outcomes.length) * 60; | |
ctx.fillStyle = `hsla(${hue}, 80%, 60%, 0.7)`; | |
ctx.fillRect(i * barWidth, height - barHeight - 30, barWidth - 2, barHeight); | |
// Label for bar | |
if (barWidth > 25) { | |
ctx.fillStyle = 'white'; | |
ctx.font = '10px Arial'; | |
ctx.textAlign = 'center'; | |
ctx.textBaseline = 'top'; | |
ctx.fillText(outcome, i * barWidth + barWidth / 2, height - 25); | |
} | |
} | |
// Draw axes | |
ctx.strokeStyle = 'rgba(200, 200, 255, 0.5)'; | |
ctx.lineWidth = 1; | |
// X axis | |
ctx.beginPath(); | |
ctx.moveTo(0, height - 30); | |
ctx.lineTo(width, height - 30); | |
ctx.stroke(); | |
// Y axis | |
ctx.beginPath(); | |
ctx.moveTo(0, 0); | |
ctx.lineTo(0, height - 30); | |
ctx.stroke(); | |
// Y axis label | |
ctx.save(); | |
ctx.translate(15, height / 2); | |
ctx.rotate(-Math.PI / 2); | |
ctx.fillStyle = 'rgba(200, 200, 255, 0.8)'; | |
ctx.font = '12px Arial'; | |
ctx.textAlign = 'center'; | |
ctx.fillText('Counts', 0, 0); | |
ctx.restore(); | |
// X axis label | |
ctx.fillStyle = 'rgba(200, 200, 255, 0.8)'; | |
ctx.font = '12px Arial'; | |
ctx.textAlign = 'center'; | |
ctx.textBaseline = 'top'; | |
ctx.fillText('Measurement Outcomes', width / 2, height - 10); | |
} | |
// Draw state evolution visualization | |
function drawEvolution() { | |
const ctx = contexts.evolution; | |
const width = canvases.evolution.width; | |
const height = canvases.evolution.height; | |
ctx.clearRect(0, 0, width, height); | |
// Create gradient background | |
const gradient = ctx.createRadialGradient(width / 2, height / 2, 0, width / 2, height / 2, height / 2); | |
gradient.addColorStop(0, 'rgba(10, 20, 40, 0.8)'); | |
gradient.addColorStop(1, 'rgba(0, 5, 20, 0.8)'); | |
ctx.fillStyle = gradient; | |
ctx.fillRect(0, 0, width, height); | |
// Draw evolution effect | |
const time = Date.now() / 1000; | |
const centerX = width / 2; | |
const centerY = height / 2; | |
// Draw quantum state amplitudes | |
const stateCount = Math.pow(2, state.qubits); | |
const radius = Math.min(width, height) * 0.35; | |
for (let i = 0; i < stateCount; i++) { | |
const angle = (i / stateCount) * Math.PI * 2; | |
const x = centerX + Math.cos(angle) * radius; | |
const y = centerY + Math.sin(angle) * radius; | |
// Amplitude visualization | |
const amplitude = 0.3 + 0.7 * Math.abs(Math.sin(time + i * 0.7)); | |
// State vector | |
ctx.strokeStyle = `rgba(100, 200, 255, ${amplitude * 0.7})`; | |
ctx.lineWidth = 2; | |
ctx.beginPath(); | |
ctx.moveTo(centerX, centerY); | |
ctx.lineTo(x, y); | |
ctx.stroke(); | |
// Amplitude point | |
ctx.fillStyle = `rgba(150, 220, 255, ${amplitude})`; | |
ctx.beginPath(); | |
ctx.arc(x, y, 5 + amplitude * 3, 0, Math.PI * 2); | |
ctx.fill(); | |
// State label | |
const bitString = i.toString(2).padStart(state.qubits, '0'); | |
ctx.fillStyle = 'rgba(255, 255, 255, 0.7)'; | |
ctx.font = '10px Arial'; | |
ctx.textAlign = 'center'; | |
ctx.textBaseline = 'middle'; | |
const textX = centerX + Math.cos(angle) * (radius + 15); | |
const textY = centerY + Math.sin(angle) * (radius + 15); | |
ctx.fillText(bitString, textX, textY); | |
} | |
// Draw environment influence (adaptive effects) | |
const envInfluence = Math.sin(time * 0.5) * 0.5 + 0.5; | |
// Draw interference pattern | |
ctx.strokeStyle = `rgba(100, 255, 200, ${0.1 + envInfluence * 0.2})`; | |
ctx.lineWidth = 1; | |
for (let i = 0; i < stateCount; i++) { | |
const angle1 = (i / stateCount) * Math.PI * 2; | |
const x1 = centerX + Math.cos(angle1) * radius; | |
const y1 = centerY + Math.sin(angle1) * radius; | |
for (let j = i + 1; j < stateCount; j++) { | |
// Only connect interfering states | |
if ((i ^ j) & 1) { | |
const angle2 = (j / stateCount) * Math.PI * 2; | |
const x2 = centerX + Math.cos(angle2) * radius; | |
const y2 = centerY + Math.sin(angle2) * radius; | |
// Draw interference line | |
ctx.beginPath(); | |
ctx.moveTo(x1, y1); | |
ctx.lineTo(x2, y2); | |
ctx.stroke(); | |
} | |
} | |
} | |
// Draw holographic projection | |
ctx.fillStyle = `rgba(100, 200, 255, ${0.05 + envInfluence * 0.1})`; | |
for (let i = 0; i < 3; i++) { | |
const projRadius = radius * (0.5 + i * 0.2); | |
ctx.beginPath(); | |
ctx.arc(centerX, centerY, projRadius, 0, Math.PI * 2); | |
ctx.fill(); | |
} | |
// Update metrics based on time | |
const fidelity = 0.5 + 0.3 * Math.sin(time * 0.3); | |
const entropy = 1 + 0.3 * Math.cos(time * 0.7); | |
const entanglement = 0.7 + 0.2 * Math.sin(time * 0.5); | |
const coherence = 0.5 + 0.2 * Math.cos(time * 0.4); | |
// Update metrics display | |
document.getElementById('fidelity-value').textContent = fidelity.toFixed(2); | |
document.getElementById('entropy-value').textContent = entropy.toFixed(2); | |
document.getElementById('entanglement-value').textContent = entanglement.toFixed(2); | |
document.getElementById('coherence-value').textContent = coherence.toFixed(2); | |
} | |
// Initialize multiplayer functionality | |
function initMultiplayer() { | |
// Get client info | |
clientId = room.party.client.id; | |
username = room.party.client.username; | |
// Setup message handler | |
room.onmessage = (event) => { | |
const data = event.data; | |
switch (data.type) { | |
case "connected": | |
addUserToList(data.clientId, data.username); | |
addSystemMessage(`${data.username} joined the simulation`); | |
break; | |
case "disconnected": | |
removeUserFromList(data.clientId); | |
removeCursor(data.clientId); | |
addSystemMessage(`${data.username} left the simulation`); | |
break; | |
case "chat": | |
addChatMessage(data.username, data.message); | |
break; | |
case "cursor_move": | |
updateCursor(data.clientId, data.x, data.y, data.username); | |
break; | |
case "parameter_change": | |
handleParameterChange(data.parameter, data.value, data.username); | |
break; | |
case "run_circuit": | |
handleRunCircuit(data.username); | |
break; | |
case "update_env": | |
handleUpdateEnv(data.username); | |
break; | |
case "run_analysis": | |
handleRunAnalysis(data.username); | |
break; | |
} | |
}; | |
// Listen for mouse movement to broadcast cursor position | |
document.addEventListener('mousemove', (e) => { | |
const now = Date.now(); | |
if (now - lastCursorUpdate > 50) { // Limit updates to every 50ms | |
lastCursorUpdate = now; | |
room.send({ | |
type: "cursor_move", | |
x: e.clientX, | |
y: e.clientY | |
}); | |
} | |
}); | |
// Setup chat functionality | |
document.getElementById('chat-send').addEventListener('click', sendChatMessage); | |
document.getElementById('chat-input').addEventListener('keypress', (e) => { | |
if (e.key === 'Enter') { | |
sendChatMessage(); | |
} | |
}); | |
// Update existing button handlers to broadcast events | |
document.getElementById('run-circuit').addEventListener('click', function() { | |
// Existing handler code | |
drawCircuit(); | |
setTimeout(() => { | |
drawHistogram(); | |
drawEvolution(); | |
}, 500); | |
// Broadcast the event | |
room.send({ | |
type: "run_circuit" | |
}); | |
}); | |
document.getElementById('update-env').addEventListener('click', function() { | |
// Existing handler code | |
drawEnvironment(); | |
setTimeout(() => { | |
drawEvolution(); | |
}, 500); | |
// Broadcast the event | |
room.send({ | |
type: "update_env" | |
}); | |
}); | |
document.getElementById('run-analysis').addEventListener('click', function() { | |
// Existing handler code | |
drawCircuit(); | |
setTimeout(() => { | |
drawEnvironment(); | |
setTimeout(() => { | |
drawHistogram(); | |
setTimeout(() => { | |
drawEvolution(); | |
}, 300); | |
}, 300); | |
}, 300); | |
// Broadcast the event | |
room.send({ | |
type: "run_analysis" | |
}); | |
}); | |
// Update input handlers to broadcast parameter changes | |
const parameterInputs = [ | |
'num-qubits', 'num-shots', 'time-const', | |
'space-scale', 'mass-value', 'dimension-num' | |
]; | |
parameterInputs.forEach(inputId => { | |
const inputElement = document.getElementById(inputId); | |
// Store original event handler | |
const originalHandler = inputElement.onchange; | |
// Add new handler that broadcasts changes | |
inputElement.addEventListener('change', function(e) { | |
// Call original handler if it exists | |
if (originalHandler) originalHandler(e); | |
// Broadcast parameter change | |
room.send({ | |
type: "parameter_change", | |
parameter: inputId, | |
value: parseInt(e.target.value) | |
}); | |
}); | |
}); | |
// Initialize users list | |
updateUsersList(); | |
} | |
// Add user to the connected users list | |
function addUserToList(userId, userName) { | |
if (!userColors[userId]) { | |
// Assign a random color for this user | |
const hue = Math.floor(Math.random() * 360); | |
userColors[userId] = `hsl(${hue}, 70%, 60%)`; | |
} | |
updateUsersList(); | |
} | |
// Remove user from the connected users list | |
function removeUserFromList(userId) { | |
updateUsersList(); | |
} | |
// Update the entire users list | |
function updateUsersList() { | |
const usersContainer = document.getElementById('users-container'); | |
usersContainer.innerHTML = ''; | |
for (const clientId in room.party.peers) { | |
const { username } = room.party.peers[clientId]; | |
const color = userColors[clientId] || 'hsl(200, 70%, 60%)'; | |
const userDiv = document.createElement('div'); | |
userDiv.className = 'user-container'; | |
const userAvatar = document.createElement('div'); | |
userAvatar.className = 'user-avatar'; | |
userAvatar.style.backgroundColor = color; | |
const userNameSpan = document.createElement('span'); | |
userNameSpan.textContent = username; | |
userDiv.appendChild(userAvatar); | |
userDiv.appendChild(userNameSpan); | |
usersContainer.appendChild(userDiv); | |
} | |
} | |
// Update cursor position for a specific user | |
function updateCursor(userId, x, y, userName) { | |
// Don't show the current user's cursor | |
if (userId === clientId) return; | |
let cursor = cursors[userId]; | |
if (!cursor) { | |
// Create cursor element if it doesn't exist | |
cursor = document.createElement('div'); | |
cursor.className = 'user-cursor'; | |
const pointer = document.createElement('div'); | |
pointer.className = 'cursor-pointer'; | |
const label = document.createElement('div'); | |
label.className = 'cursor-label'; | |
label.textContent = userName; | |
cursor.appendChild(pointer); | |
cursor.appendChild(label); | |
document.body.appendChild(cursor); | |
cursors[userId] = cursor; | |
} | |
// Get user's color or assign a new one | |
if (!userColors[userId]) { | |
const hue = Math.floor(Math.random() * 360); | |
userColors[userId] = `hsl(${hue}, 70%, 60%)`; | |
} | |
// Update cursor position and color | |
cursor.style.left = `${x}px`; | |
cursor.style.top = `${y}px`; | |
cursor.querySelector('.cursor-pointer').style.backgroundColor = userColors[userId]; | |
} | |
// Remove cursor for a disconnected user | |
function removeCursor(userId) { | |
if (cursors[userId]) { | |
document.body.removeChild(cursors[userId]); | |
delete cursors[userId]; | |
} | |
} | |
// Send a chat message | |
function sendChatMessage() { | |
const inputElement = document.getElementById('chat-input'); | |
const message = inputElement.value.trim(); | |
if (message) { | |
room.send({ | |
type: "chat", | |
message: message | |
}); | |
addChatMessage(username, message); | |
inputElement.value = ''; | |
} | |
} | |
// Add a message to the chat box | |
function addChatMessage(userName, message) { | |
const messagesContainer = document.getElementById('chat-messages'); | |
const messageDiv = document.createElement('div'); | |
messageDiv.className = 'message'; | |
const userSpan = document.createElement('span'); | |
userSpan.className = 'message-user'; | |
userSpan.textContent = `${userName}: `; | |
const textSpan = document.createElement('span'); | |
textSpan.className = 'message-text'; | |
textSpan.textContent = message; | |
messageDiv.appendChild(userSpan); | |
messageDiv.appendChild(textSpan); | |
messagesContainer.appendChild(messageDiv); | |
// Auto-scroll to the bottom | |
messagesContainer.scrollTop = messagesContainer.scrollHeight; | |
} | |
// Add a system message | |
function addSystemMessage(message) { | |
const messagesContainer = document.getElementById('chat-messages'); | |
const messageDiv = document.createElement('div'); | |
messageDiv.className = 'message'; | |
messageDiv.style.color = '#8cf'; | |
messageDiv.style.fontStyle = 'italic'; | |
messageDiv.textContent = message; | |
messagesContainer.appendChild(messageDiv); | |
messagesContainer.scrollTop = messagesContainer.scrollHeight; | |
} | |
// Handle parameter change events from other users | |
function handleParameterChange(paramId, value, userName) { | |
const inputElement = document.getElementById(paramId); | |
if (inputElement) { | |
inputElement.value = value; | |
// Update the state accordingly | |
if (paramId === 'num-qubits') { | |
state.qubits = value; | |
drawCircuit(); | |
drawHistogram(); | |
drawEvolution(); | |
} else if (paramId === 'num-shots') { | |
state.shots = value; | |
drawHistogram(); | |
} else if (paramId === 'time-const') { | |
state.environmentParams.t = value; | |
drawEnvironment(); | |
} else if (paramId === 'space-scale') { | |
state.environmentParams.s = value; | |
drawEnvironment(); | |
} else if (paramId === 'mass-value') { | |
state.environmentParams.m = value; | |
drawEnvironment(); | |
} else if (paramId === 'dimension-num') { | |
state.environmentParams.d = value; | |
drawEnvironment(); | |
} | |
addSystemMessage(`${userName} changed ${paramId} to ${value}`); | |
} | |
} | |
// Handle run circuit events from other users | |
function handleRunCircuit(userName) { | |
addSystemMessage(`${userName} ran the quantum circuit`); | |
} | |
// Handle update environment events from other users | |
function handleUpdateEnv(userName) { | |
addSystemMessage(`${userName} updated the environment`); | |
} | |
// Handle run analysis events from other users | |
function handleRunAnalysis(userName) { | |
addSystemMessage(`${userName} ran a full analysis`); | |
} | |
// Modify the initialize function to include multiplayer | |
function initialize() { | |
// Set up input event handlers | |
document.getElementById('num-qubits').addEventListener('change', function(e) { | |
state.qubits = parseInt(e.target.value); | |
drawCircuit(); | |
drawHistogram(); | |
drawEvolution(); | |
}); | |
document.getElementById('num-shots').addEventListener('change', function(e) { | |
state.shots = parseInt(e.target.value); | |
drawHistogram(); | |
}); | |
document.getElementById('time-const').addEventListener('change', function(e) { | |
state.environmentParams.t = parseInt(e.target.value); | |
drawEnvironment(); | |
}); | |
document.getElementById('space-scale').addEventListener('change', function(e) { | |
state.environmentParams.s = parseInt(e.target.value); | |
drawEnvironment(); | |
}); | |
document.getElementById('mass-value').addEventListener('change', function(e) { | |
state.environmentParams.m = parseInt(e.target.value); | |
drawEnvironment(); | |
}); | |
document.getElementById('dimension-num').addEventListener('change', function(e) { | |
state.environmentParams.d = parseInt(e.target.value); | |
drawEnvironment(); | |
}); | |
// Set up button event handlers | |
document.getElementById('run-circuit').addEventListener('click', function() { | |
// Simulate running circuit | |
drawCircuit(); | |
setTimeout(() => { | |
drawHistogram(); | |
drawEvolution(); | |
}, 500); | |
}); | |
document.getElementById('update-env').addEventListener('click', function() { | |
// Simulate updating environment | |
drawEnvironment(); | |
setTimeout(() => { | |
drawEvolution(); | |
}, 500); | |
}); | |
document.getElementById('run-analysis').addEventListener('click', function() { | |
// Simulate full analysis | |
drawCircuit(); | |
setTimeout(() => { | |
drawEnvironment(); | |
setTimeout(() => { | |
drawHistogram(); | |
setTimeout(() => { | |
drawEvolution(); | |
}, 300); | |
}, 300); | |
}, 300); | |
}); | |
// Initial draw | |
drawCircuit(); | |
drawEnvironment(); | |
drawHistogram(); | |
drawEvolution(); | |
// Animation loop for evolution visualization | |
function animate() { | |
drawEvolution(); | |
requestAnimationFrame(animate); | |
} | |
animate(); | |
// Initialize multiplayer functionality | |
initMultiplayer(); | |
} | |
// Handle window resize | |
window.addEventListener('resize', function() { | |
for (const canvas of Object.values(canvases)) { | |
canvas.width = canvas.clientWidth; | |
canvas.height = canvas.clientHeight; | |
} | |
drawCircuit(); | |
drawEnvironment(); | |
drawHistogram(); | |
drawEvolution(); | |
}); | |
// Start the simulation | |
initialize(); | |
</script> | |
</body> | |
</html> | |