Spaces:
Running
on
Zero
Running
on
Zero
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>๐งฌ DNA CASINO - Protein Synthesis Slot Machine</title> | |
<style> | |
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Bebas+Neue&display=swap'); | |
* { | |
margin: 0; | |
padding: 0; | |
box-sizing: border-box; | |
} | |
body { | |
background: #000; | |
color: #fff; | |
font-family: 'Orbitron', monospace; | |
overflow-x: hidden; | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: flex-start; | |
min-height: 100vh; | |
position: relative; | |
padding-top: 10px; | |
} | |
/* Casino carpet pattern background */ | |
body::before { | |
content: ''; | |
position: fixed; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
background: | |
radial-gradient(circle at 20% 30%, #ff0066 0px, transparent 100px), | |
radial-gradient(circle at 80% 70%, #00ffff 0px, transparent 100px), | |
radial-gradient(circle at 50% 50%, #ffff00 0px, transparent 150px), | |
radial-gradient(circle at 30% 80%, #ff00ff 0px, transparent 80px), | |
radial-gradient(circle at 70% 20%, #00ff00 0px, transparent 90px); | |
opacity: 0.1; | |
z-index: 0; | |
animation: casinoLights 10s ease-in-out infinite; | |
} | |
@keyframes casinoLights { | |
0%, 100% { filter: hue-rotate(0deg) brightness(1); } | |
50% { filter: hue-rotate(180deg) brightness(1.5); } | |
} | |
/* Static TV noise overlay */ | |
body::after { | |
content: ''; | |
position: fixed; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
background-image: | |
repeating-linear-gradient( | |
0deg, | |
transparent 0px, | |
rgba(255,255,255,0.08) 1px, | |
transparent 1px, | |
transparent 2px | |
), | |
repeating-linear-gradient( | |
90deg, | |
transparent 0px, | |
rgba(0,0,0,0.05) 1px, | |
transparent 1px, | |
transparent 2px | |
); | |
background-size: 2px 2px, 2px 2px; | |
pointer-events: none; | |
z-index: 1; | |
opacity: 0.8; | |
animation: staticNoise 0.1s steps(8) infinite; | |
} | |
@keyframes staticNoise { | |
0%, 100% { transform: translate(0, 0); } | |
50% { transform: translate(-1px, -1px); } | |
} | |
/* Main casino cabinet */ | |
.machine-container { | |
background: linear-gradient(145deg, #1a0000, #330000); | |
border: 5px solid #ffd700; | |
border-radius: 30px; | |
padding: 20px; | |
padding-right: 120px; | |
box-shadow: | |
0 0 50px rgba(255, 215, 0, 0.5), | |
0 20px 40px rgba(0,0,0,0.8), | |
inset 0 0 30px rgba(255, 215, 0, 0.2); | |
width: 95vw; | |
max-width: 1400px; | |
position: relative; | |
z-index: 2; | |
} | |
/* Blinking lights around the machine */ | |
.machine-container::before { | |
content: ''; | |
position: absolute; | |
top: -10px; | |
left: -10px; | |
right: -10px; | |
bottom: -10px; | |
border-radius: 35px; | |
background: conic-gradient( | |
from 0deg, | |
#ff0000, #ff7700, #ffff00, #00ff00, | |
#0000ff, #ff00ff, #ff0000 | |
); | |
z-index: -1; | |
animation: rotateGradient 3s linear infinite; | |
filter: blur(10px); | |
opacity: 0.7; | |
} | |
@keyframes rotateGradient { | |
0% { transform: rotate(0deg); } | |
100% { transform: rotate(360deg); } | |
} | |
/* Neon title */ | |
.title { | |
text-align: center; | |
font-family: 'Bebas Neue', cursive; | |
font-size: 4rem; | |
margin-bottom: 10px; | |
position: relative; | |
text-transform: uppercase; | |
letter-spacing: 0.2em; | |
} | |
.title a { | |
text-decoration: none; | |
color: #fff; | |
text-shadow: | |
0 0 10px #ff00ff, | |
0 0 20px #ff00ff, | |
0 0 30px #ff00ff, | |
0 0 40px #ff00ff, | |
0 0 70px #ff00ff, | |
0 0 80px #ff00ff, | |
0 0 100px #ff00ff, | |
0 0 150px #ff00ff; | |
animation: neonFlicker 1.5s infinite alternate; | |
} | |
@keyframes neonFlicker { | |
0%, 100% { opacity: 1; } | |
33% { opacity: 0.8; } | |
66% { opacity: 0.9; } | |
} | |
.subtitle { | |
text-align: center; | |
font-size: 1.5rem; | |
color: #ffd700; | |
margin-bottom: 20px; | |
text-shadow: 0 0 10px #ffd700; | |
animation: pulse 2s ease-in-out infinite; | |
} | |
@keyframes pulse { | |
0%, 100% { transform: scale(1); } | |
50% { transform: scale(1.05); } | |
} | |
/* Jackpot counter */ | |
.jackpot-display { | |
background: #000; | |
border: 3px solid #ffd700; | |
border-radius: 10px; | |
padding: 10px 20px; | |
margin: 10px auto; | |
text-align: center; | |
max-width: 400px; | |
box-shadow: | |
inset 0 0 20px rgba(255, 215, 0, 0.3), | |
0 0 20px rgba(255, 215, 0, 0.5); | |
} | |
.jackpot-label { | |
font-size: 0.9rem; | |
color: #ffd700; | |
text-transform: uppercase; | |
letter-spacing: 2px; | |
} | |
.jackpot-value { | |
font-size: 2rem; | |
color: #00ff00; | |
font-weight: bold; | |
text-shadow: 0 0 10px #00ff00; | |
font-family: 'Orbitron', monospace; | |
} | |
/* Language selector */ | |
.language-selector { | |
display: flex; | |
justify-content: center; | |
margin: 20px 0; | |
} | |
.language-toggle { | |
position: relative; | |
display: inline-block; | |
width: 120px; | |
height: 40px; | |
cursor: pointer; | |
} | |
.language-toggle input { | |
opacity: 0; | |
width: 0; | |
height: 0; | |
} | |
.toggle-slider { | |
position: absolute; | |
top: 0; | |
left: 0; | |
right: 0; | |
bottom: 0; | |
background: linear-gradient(145deg, #444, #222); | |
border: 2px solid #ffd700; | |
border-radius: 40px; | |
transition: all 0.4s; | |
display: flex; | |
align-items: center; | |
justify-content: space-between; | |
padding: 0 10px; | |
box-shadow: | |
0 5px 15px rgba(0,0,0,0.5), | |
inset 0 2px 5px rgba(255,255,255,0.1); | |
} | |
.lang-label { | |
font-size: 0.9rem; | |
font-weight: bold; | |
color: #888; | |
transition: all 0.3s; | |
z-index: 2; | |
} | |
.lang-label[data-lang="en"] { | |
margin-left: 5px; | |
} | |
.lang-label[data-lang="ko"] { | |
margin-right: 5px; | |
} | |
.toggle-slider::after { | |
content: ''; | |
position: absolute; | |
height: 32px; | |
width: 50px; | |
left: 4px; | |
bottom: 4px; | |
background: linear-gradient(145deg, #00ff00, #00cc00); | |
border-radius: 32px; | |
transition: all 0.4s; | |
box-shadow: | |
0 2px 10px rgba(0,255,0,0.5), | |
inset 0 -2px 5px rgba(0,0,0,0.3); | |
} | |
.language-toggle input:checked + .toggle-slider::after { | |
transform: translateX(62px); | |
background: linear-gradient(145deg, #ff4444, #cc0000); | |
box-shadow: | |
0 2px 10px rgba(255,0,0,0.5), | |
inset 0 -2px 5px rgba(0,0,0,0.3); | |
} | |
.language-toggle input:checked + .toggle-slider .lang-label[data-lang="ko"] { | |
color: #fff; | |
} | |
.language-toggle input:not(:checked) + .toggle-slider .lang-label[data-lang="en"] { | |
color: #fff; | |
} | |
.cell-type-selector { | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
gap: 20px; | |
margin-bottom: 20px; | |
} | |
.radio-group { | |
display: flex; | |
gap: 30px; | |
} | |
.chip-label { | |
position: relative; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
width: 100px; | |
height: 100px; | |
cursor: pointer; | |
transition: all 0.3s ease; | |
} | |
.chip-label input[type="radio"] { | |
position: absolute; | |
opacity: 0; | |
} | |
.chip { | |
position: relative; | |
width: 100%; | |
height: 100%; | |
border-radius: 50%; | |
background: radial-gradient(circle at 30% 30%, #ff4444, #cc0000); | |
box-shadow: | |
0 5px 15px rgba(0,0,0,0.5), | |
inset 0 0 20px rgba(0,0,0,0.3), | |
inset -3px -3px 10px rgba(255,255,255,0.2); | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
font-weight: bold; | |
font-size: 1.1rem; | |
color: #fff; | |
text-shadow: 2px 2px 4px rgba(0,0,0,0.5); | |
border: 8px dashed #fff; | |
animation: chipRotate 20s linear infinite; | |
} | |
@keyframes chipRotate { | |
0% { transform: rotate(0deg); } | |
100% { transform: rotate(360deg); } | |
} | |
.chip-label:hover .chip { | |
transform: scale(1.1); | |
box-shadow: | |
0 10px 30px rgba(255,0,0,0.5), | |
inset 0 0 30px rgba(255,255,255,0.3); | |
} | |
.chip-label input[type="radio"]:checked + .chip { | |
background: radial-gradient(circle at 30% 30%, #44ff44, #00cc00); | |
box-shadow: | |
0 0 30px rgba(0,255,0,0.7), | |
inset 0 0 20px rgba(0,0,0,0.3); | |
animation: chipRotate 5s linear infinite, winPulse 1s ease-in-out infinite; | |
} | |
@keyframes winPulse { | |
0%, 100% { transform: scale(1); } | |
50% { transform: scale(1.05); } | |
} | |
/* Slot machine reels */ | |
.reels-container { | |
background: #000; | |
border: 5px solid #ffd700; | |
border-radius: 15px; | |
padding: 20px; | |
max-width: 100%; | |
position: relative; | |
box-shadow: | |
inset 0 0 50px rgba(0,0,0,0.8), | |
0 0 30px rgba(255, 215, 0, 0.5); | |
overflow: visible; | |
margin: 20px 0; | |
} | |
.reels-wrapper { | |
display: flex; | |
gap: 2px; | |
min-width: fit-content; | |
padding: 5px 0; | |
justify-content: center; | |
flex-wrap: wrap; | |
max-width: 1200px; | |
margin: 0 auto; | |
background: linear-gradient(90deg, | |
transparent 0%, | |
rgba(255, 215, 0, 0.1) 50%, | |
transparent 100%); | |
} | |
.reel { | |
width: 20px; | |
height: 50px; | |
background: #ffffff; | |
border: 2px solid #ffd700; | |
border-radius: 3px; | |
overflow: hidden; | |
position: relative; | |
box-shadow: | |
inset 0 0 10px rgba(0,0,0,0.3), | |
0 0 5px rgba(255, 215, 0, 0.5); | |
} | |
.reel-strip { | |
position: absolute; | |
width: 100%; | |
transition: transform 0.5s ease-out; | |
} | |
.nucleotide { | |
height: 50px; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
font-size: 1.2rem; | |
font-weight: bold; | |
background: #ffffff; | |
text-shadow: 0 0 5px currentColor; | |
} | |
.nucleotide.A { color: #00ff00; background: #001100; } | |
.nucleotide.T { color: #ff0000; background: #110000; } | |
.nucleotide.C { color: #00ffff; background: #001111; } | |
.nucleotide.G { color: #ffff00; background: #111100; } | |
/* Casino style lever */ | |
.lever-container { | |
position: absolute; | |
right: -90px; | |
top: 50%; | |
transform: translateY(-50%); | |
z-index: 3; | |
width: 80px; | |
height: 250px; | |
} | |
.lever { | |
width: 100%; | |
height: 100%; | |
position: relative; | |
cursor: pointer; | |
} | |
.lever-mount { | |
position: absolute; | |
top: 100px; | |
left: -15px; | |
width: 60px; | |
height: 80px; | |
background: linear-gradient(180deg, #ffd700, #b8860b); | |
border-radius: 10px 0 0 10px; | |
box-shadow: | |
0 5px 20px rgba(0,0,0,0.5), | |
inset 0 2px 5px rgba(255,255,255,0.5); | |
} | |
.lever-arm { | |
position: absolute; | |
top: 30px; | |
left: 10px; | |
width: 15px; | |
height: 100px; | |
background: linear-gradient(180deg, #c0c0c0, #808080); | |
border-radius: 8px; | |
box-shadow: 0 3px 10px rgba(0,0,0,0.5); | |
transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55); | |
} | |
.lever-ball { | |
position: absolute; | |
top: -40px; | |
left: 50%; | |
transform: translateX(-50%); | |
width: 80px; | |
height: 80px; | |
background: radial-gradient(circle at 35% 35%, #ff4444, #cc0000, #800000); | |
border-radius: 50%; | |
box-shadow: | |
0 10px 30px rgba(0,0,0,0.6), | |
inset -10px -10px 20px rgba(0,0,0,0.5), | |
inset 5px 5px 10px rgba(255,255,255,0.6); | |
position: relative; | |
overflow: hidden; | |
} | |
.lever-ball::before { | |
content: '777'; | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
font-size: 1.5rem; | |
font-weight: bold; | |
color: #ffd700; | |
text-shadow: 0 2px 4px rgba(0,0,0,0.8); | |
} | |
.lever.pulled .lever-arm { | |
transform: translateY(100px); | |
height: 20px; | |
} | |
/* Generate button */ | |
.spin-button { | |
background: linear-gradient(145deg, #ff0000, #cc0000); | |
border: 3px solid #ffd700; | |
padding: 25px 80px; | |
font-size: 2rem; | |
font-weight: bold; | |
border-radius: 50px; | |
cursor: pointer; | |
text-transform: uppercase; | |
letter-spacing: 3px; | |
box-shadow: | |
0 10px 30px rgba(0,0,0,0.7), | |
0 0 30px rgba(255, 0, 0, 0.5), | |
inset 0 2px 10px rgba(255,255,255,0.3); | |
transition: all 0.3s ease; | |
color: #fff; | |
text-shadow: 0 3px 6px rgba(0,0,0,0.5); | |
position: relative; | |
overflow: hidden; | |
font-family: 'Bebas Neue', cursive; | |
} | |
.spin-button::before { | |
content: ''; | |
position: absolute; | |
top: -50%; | |
left: -50%; | |
width: 200%; | |
height: 200%; | |
background: linear-gradient(45deg, | |
transparent 30%, | |
rgba(255,255,255,0.3) 50%, | |
transparent 70%); | |
animation: buttonShine 3s infinite; | |
} | |
@keyframes buttonShine { | |
0% { transform: rotate(0deg); } | |
100% { transform: rotate(360deg); } | |
} | |
.spin-button:hover { | |
transform: translateY(-3px) scale(1.05); | |
box-shadow: | |
0 15px 40px rgba(0,0,0,0.8), | |
0 0 50px rgba(255, 0, 0, 0.7); | |
} | |
.spin-button:active { | |
transform: translateY(0) scale(0.98); | |
} | |
.spin-button:disabled { | |
background: linear-gradient(145deg, #444, #222); | |
cursor: not-allowed; | |
box-shadow: none; | |
animation: none; | |
} | |
/* Sequence display */ | |
.sequence-display { | |
background: #000; | |
border: 3px solid #00ff00; | |
border-radius: 15px; | |
padding: 25px; | |
font-family: 'Orbitron', monospace; | |
font-size: 0.9rem; | |
letter-spacing: 2px; | |
width: 100%; | |
max-width: 1200px; | |
margin: 20px auto; | |
box-shadow: | |
0 0 30px rgba(0, 255, 0, 0.5), | |
inset 0 0 20px rgba(0, 255, 0, 0.1); | |
position: relative; | |
min-height: 100px; | |
word-wrap: break-word; | |
word-break: break-all; | |
white-space: pre-wrap; | |
overflow-wrap: break-word; | |
} | |
.sequence-display::before { | |
content: '๐งฌ SYNTHETIC REGULATORY ELEMENT'; | |
position: absolute; | |
top: -15px; | |
left: 50%; | |
transform: translateX(-50%); | |
background: #000; | |
padding: 0 20px; | |
font-size: 0.8rem; | |
color: #00ff00; | |
letter-spacing: 3px; | |
text-shadow: 0 0 10px #00ff00; | |
} | |
/* Protein analysis panel */ | |
.protein-panel { | |
background: linear-gradient(145deg, #1a0033, #000033); | |
border: 3px solid #00ffff; | |
border-radius: 15px; | |
padding: 25px; | |
margin: 20px auto; | |
max-width: 1200px; | |
box-shadow: | |
0 0 30px rgba(0, 255, 255, 0.5), | |
inset 0 0 20px rgba(0, 255, 255, 0.1); | |
display: none; | |
animation: fadeIn 0.5s ease-in; | |
} | |
@keyframes fadeIn { | |
from { opacity: 0; transform: translateY(20px); } | |
to { opacity: 1; transform: translateY(0); } | |
} | |
.protein-panel.active { | |
display: block; | |
} | |
.protein-header { | |
font-size: 1.5rem; | |
color: #00ffff; | |
margin-bottom: 15px; | |
text-align: center; | |
text-transform: uppercase; | |
letter-spacing: 3px; | |
text-shadow: 0 0 15px #00ffff; | |
} | |
.protein-sequence { | |
background: #000; | |
border: 1px solid #00ffff; | |
border-radius: 10px; | |
padding: 15px; | |
margin-bottom: 20px; | |
font-family: 'Courier New', monospace; | |
font-size: 0.85rem; | |
word-wrap: break-word; | |
word-break: break-all; | |
color: #00ff00; | |
max-height: 150px; | |
overflow-y: auto; | |
white-space: pre-wrap; | |
line-height: 1.4; | |
} | |
.protein-analysis { | |
background: rgba(0, 255, 255, 0.05); | |
border: 1px solid rgba(0, 255, 255, 0.3); | |
border-radius: 10px; | |
padding: 20px; | |
color: #fff; | |
line-height: 1.8; | |
font-size: 0.95rem; | |
} | |
.protein-analysis h3 { | |
color: #00ffff; | |
margin-bottom: 10px; | |
text-shadow: 0 0 10px #00ffff; | |
} | |
.protein-stats { | |
display: grid; | |
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); | |
gap: 15px; | |
margin-bottom: 20px; | |
} | |
.stat-card { | |
background: rgba(255, 215, 0, 0.1); | |
border: 1px solid #ffd700; | |
border-radius: 10px; | |
padding: 15px; | |
text-align: center; | |
} | |
.stat-label { | |
font-size: 0.8rem; | |
color: #ffd700; | |
text-transform: uppercase; | |
letter-spacing: 1px; | |
} | |
.stat-value { | |
font-size: 1.5rem; | |
color: #fff; | |
font-weight: bold; | |
margin-top: 5px; | |
} | |
/* Info footer */ | |
.info { | |
text-align: center; | |
margin-top: 20px; | |
color: #ffd700; | |
font-size: 1rem; | |
text-transform: uppercase; | |
letter-spacing: 2px; | |
text-shadow: 0 0 10px #ffd700; | |
} | |
.lab-credit { | |
text-align: center; | |
margin-top: 15px; | |
font-size: 1.3rem; | |
display: flex; | |
justify-content: center; | |
gap: 40px; | |
flex-wrap: wrap; | |
} | |
.lab-credit a { | |
color: #00ff00; | |
text-decoration: none; | |
font-weight: bold; | |
letter-spacing: 2px; | |
padding: 10px 30px; | |
border: 2px solid #00ff00; | |
border-radius: 30px; | |
display: inline-block; | |
transition: all 0.3s ease; | |
text-shadow: 0 0 10px #00ff00; | |
} | |
.lab-credit a:nth-child(2) { | |
color: #ffff00; | |
border-color: #ffff00; | |
text-shadow: 0 0 10px #ffff00; | |
} | |
.lab-credit a:hover { | |
background: #00ff00; | |
color: #000; | |
box-shadow: 0 0 30px #00ff00; | |
transform: scale(1.1); | |
} | |
.lab-credit a:nth-child(2):hover { | |
background: #ffff00; | |
box-shadow: 0 0 30px #ffff00; | |
} | |
/* Winning animation */ | |
.jackpot-win { | |
animation: jackpotFlash 2s ease-out; | |
} | |
@keyframes jackpotFlash { | |
0%, 100% { background-color: transparent; } | |
10%, 30%, 50%, 70%, 90% { background-color: rgba(255, 215, 0, 0.3); } | |
20%, 40%, 60%, 80% { background-color: transparent; } | |
} | |
/* Loading animation */ | |
@keyframes continuousSpin { | |
from { transform: translateY(0); } | |
to { transform: translateY(-200px); } | |
} | |
.reel-strip.loading { | |
animation: continuousSpin 1s linear infinite; | |
} | |
/* Scrollbar styling */ | |
::-webkit-scrollbar { | |
width: 10px; | |
} | |
::-webkit-scrollbar-track { | |
background: rgba(0, 0, 0, 0.5); | |
border-radius: 5px; | |
} | |
::-webkit-scrollbar-thumb { | |
background: #00ffff; | |
border-radius: 5px; | |
} | |
::-webkit-scrollbar-thumb:hover { | |
background: #00cccc; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="machine-container"> | |
<h1 class="title"><a href="https://github.com/pinellolab/DNA-Diffusion" target="_blank" rel="noopener noreferrer">๐ฐ DNA CASINO ๐งฌ</a></h1> | |
<div class="subtitle">โ PROTEIN SYNTHESIS JACKPOT โ </div> | |
<div class="jackpot-display"> | |
<div class="jackpot-label">Current Sequence Length</div> | |
<div class="jackpot-value" id="sequenceCounter">0 BP</div> | |
</div> | |
<div class="cell-type-selector"> | |
<div class="radio-group"> | |
<label class="chip-label"> | |
<input type="radio" name="cellType" value="K562" checked> | |
<div class="chip">K562</div> | |
</label> | |
<label class="chip-label"> | |
<input type="radio" name="cellType" value="GM12878"> | |
<div class="chip">GM12878</div> | |
</label> | |
<label class="chip-label"> | |
<input type="radio" name="cellType" value="HepG2"> | |
<div class="chip">HepG2</div> | |
</label> | |
</div> | |
</div> | |
<div class="language-selector"> | |
<label class="language-toggle"> | |
<input type="checkbox" id="languageToggle"> | |
<span class="toggle-slider"> | |
<span class="lang-label" data-lang="en">EN</span> | |
<span class="lang-label" data-lang="ko">ํ๊ธ</span> | |
</span> | |
</label> | |
</div> | |
<div class="reels-container" id="reelsContainer"> | |
<div class="reels-wrapper" id="reelsWrapper"></div> | |
<div class="lever-container"> | |
<div class="lever" id="lever"> | |
<div class="lever-mount"></div> | |
<div class="lever-arm"> | |
<div class="lever-ball"></div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="controls"> | |
<button class="spin-button" id="spinButton"><span>๐ฒ SPIN TO WIN ๐ฒ</span></button> | |
<div class="sequence-display" id="sequenceDisplay"> | |
๐ฏ <span id="initialMessage">Pull the lever or press SPIN to generate your winning sequence!</span> ๐ฏ | |
</div> | |
<div class="protein-panel" id="proteinPanel"> | |
<div class="protein-header" id="proteinHeader">๐ฌ Protein Analysis Report ๐ฌ</div> | |
<div class="protein-stats" id="proteinStats"></div> | |
<div class="protein-sequence" id="proteinSequence"></div> | |
<div class="protein-analysis" id="proteinAnalysis"> | |
<h3>Analyzing protein structure...</h3> | |
</div> | |
</div> | |
</div> | |
<div class="info"> | |
๐ฐ 200BP REGULATORY ELEMENTS โข CELL-TYPE SPECIFIC โข SYNTHETIC BIOLOGY JACKPOT ๐ฐ | |
</div> | |
<div class="lab-credit"> | |
<a href="https://pinellolab.org" target="_blank" rel="noopener noreferrer"> | |
๐งฌ Based by PINELLO LAB ๐งฌ | |
</a> | |
<a href="https://discord.gg/openfreeai" target="_blank" rel="noopener noreferrer"> | |
โก Powered by VIDraft โก | |
</a> | |
</div> | |
</div> | |
<script> | |
const NUCLEOTIDES = ['A', 'T', 'C', 'G']; | |
const REEL_COUNT = 200; | |
let TARGET_SEQUENCE = ''; | |
let reels = []; | |
let isSpinning = false; | |
function generateRandomSequence() { | |
let sequence = ''; | |
for (let i = 0; i < REEL_COUNT; i++) { | |
sequence += NUCLEOTIDES[Math.floor(Math.random() * 4)]; | |
} | |
return sequence; | |
} | |
function createReel(index) { | |
const reel = document.createElement('div'); | |
reel.className = 'reel'; | |
const strip = document.createElement('div'); | |
strip.className = 'reel-strip'; | |
// Create multiple nucleotides for smooth spinning effect | |
for (let i = 0; i < 10; i++) { | |
NUCLEOTIDES.forEach(n => { | |
const nucleotide = document.createElement('div'); | |
nucleotide.className = `nucleotide ${n}`; | |
nucleotide.textContent = n; | |
strip.appendChild(nucleotide); | |
}); | |
} | |
reel.appendChild(strip); | |
return { element: reel, strip: strip, currentPosition: 0 }; | |
} | |
function initializeReels() { | |
const wrapper = document.getElementById('reelsWrapper'); | |
wrapper.innerHTML = ''; | |
reels = []; | |
for (let i = 0; i < REEL_COUNT; i++) { | |
const reel = createReel(i); | |
reels.push(reel); | |
wrapper.appendChild(reel.element); | |
// Set initial position to show a random nucleotide | |
const randomIndex = Math.floor(Math.random() * 4); | |
const initialOffset = -randomIndex * 50; | |
reel.strip.style.transform = `translateY(${initialOffset}px)`; | |
reel.currentPosition = randomIndex * 50; | |
} | |
} | |
function updateSequenceCounter(length) { | |
const counter = document.getElementById('sequenceCounter'); | |
counter.textContent = `${length} BP`; | |
counter.style.animation = 'pulse 0.5s ease-out'; | |
} | |
function startContinuousSpinning() { | |
reels.forEach((reel, index) => { | |
// Add continuous spinning animation | |
reel.strip.style.transition = 'none'; | |
reel.strip.classList.add('loading'); | |
// Add slight delay variation for visual effect | |
const delay = (index % 10) * 0.1; | |
reel.strip.style.animationDelay = `${delay}s`; | |
}); | |
// Update counter animation | |
let count = 0; | |
const counterInterval = setInterval(() => { | |
if (!isSpinning) { | |
clearInterval(counterInterval); | |
return; | |
} | |
count = (count + 7) % 201; | |
updateSequenceCounter(count); | |
}, 100); | |
} | |
function displayProteinAnalysis(metadata) { | |
const panel = document.getElementById('proteinPanel'); | |
const header = document.getElementById('proteinHeader'); | |
const statsDiv = document.getElementById('proteinStats'); | |
const sequenceDiv = document.getElementById('proteinSequence'); | |
const analysisDiv = document.getElementById('proteinAnalysis'); | |
// Show panel | |
panel.classList.add('active'); | |
// Check language | |
const isKorean = document.getElementById('languageToggle').checked; | |
// Update header | |
header.textContent = isKorean ? '๐ฌ ๋จ๋ฐฑ์ง ๋ถ์ ๋ณด๊ณ ์ ๐ฌ' : '๐ฌ Protein Analysis Report ๐ฌ'; | |
// Display stats | |
statsDiv.innerHTML = ` | |
<div class="stat-card"> | |
<div class="stat-label">${isKorean ? '์ธํฌ ํ์ ' : 'Cell Type'}</div> | |
<div class="stat-value">${metadata.cell_type || 'Unknown'}</div> | |
</div> | |
<div class="stat-card"> | |
<div class="stat-label">${isKorean ? 'DNA ๊ธธ์ด' : 'DNA Length'}</div> | |
<div class="stat-value">${TARGET_SEQUENCE.length} bp</div> | |
</div> | |
<div class="stat-card"> | |
<div class="stat-label">${isKorean ? '๋จ๋ฐฑ์ง ๊ธธ์ด' : 'Protein Length'}</div> | |
<div class="stat-value">${metadata.protein_length || 0} aa</div> | |
</div> | |
<div class="stat-card"> | |
<div class="stat-label">${isKorean ? '์์ฑ ์๊ฐ' : 'Generation Time'}</div> | |
<div class="stat-value">${(metadata.generation_time || 0).toFixed(1)}s</div> | |
</div> | |
`; | |
// Display protein sequence | |
if (metadata.protein_sequence) { | |
sequenceDiv.innerHTML = `<strong>${isKorean ? '๋จ๋ฐฑ์ง ์ํ์ค:' : 'Protein Sequence:'}</strong><br>${metadata.protein_sequence}`; | |
} | |
// Display analysis | |
if (metadata.protein_analysis) { | |
analysisDiv.innerHTML = `<h3>๐งฌ ${isKorean ? '๊ตฌ์กฐ ๋ฐ ๊ธฐ๋ฅ ๋ถ์' : 'Structural & Functional Analysis'}</h3>${metadata.protein_analysis}`; | |
} else { | |
analysisDiv.innerHTML = `<h3>โณ ${isKorean ? '๋จ๋ฐฑ์ง ๋ถ์ ๋๊ธฐ ์ค...' : 'Protein analysis pending...'}</h3>`; | |
} | |
} | |
function stopAndShowSequence(sequence, metadata = {}) { | |
TARGET_SEQUENCE = sequence; | |
updateSequenceCounter(sequence.length); | |
reels.forEach((reel, index) => { | |
// Remove continuous spinning | |
reel.strip.classList.remove('loading'); | |
// Calculate target position | |
const targetNucleotide = TARGET_SEQUENCE[index]; | |
const targetIndex = NUCLEOTIDES.indexOf(targetNucleotide); | |
const finalPosition = targetIndex * 50; | |
// Set up the final positioning animation | |
setTimeout(() => { | |
reel.strip.style.transition = `transform ${1000 + index * 5}ms cubic-bezier(0.17, 0.67, 0.12, 0.99)`; | |
reel.strip.style.transform = `translateY(${-finalPosition}px)`; | |
reel.currentPosition = finalPosition; | |
}, index * 2); | |
}); | |
// Show the complete sequence after animation | |
setTimeout(() => { | |
const container = document.getElementById('reelsContainer'); | |
const display = document.getElementById('sequenceDisplay'); | |
const button = document.getElementById('spinButton'); | |
const lever = document.getElementById('lever'); | |
container.classList.remove('spinning'); | |
container.classList.add('jackpot-win'); | |
const isKorean = document.getElementById('languageToggle').checked; | |
display.innerHTML = `<strong>๐ ${isKorean ? '์ญํ ์ํ์ค' : 'JACKPOT SEQUENCE'} ๐</strong><br><span style="color: #00ff00; font-family: 'Courier New', monospace; word-wrap: break-word; word-break: break-all; display: block; margin-top: 10px;">${TARGET_SEQUENCE}</span>`; | |
// Display protein analysis if available | |
if (metadata) { | |
displayProteinAnalysis(metadata); | |
} | |
button.disabled = false; | |
isSpinning = false; | |
// Release the lever | |
lever.classList.remove('pulled'); | |
setTimeout(() => { | |
container.classList.remove('jackpot-win'); | |
}, 2000); | |
}, 1500); | |
} | |
function startGeneration() { | |
if (isSpinning) return; | |
isSpinning = true; | |
const button = document.getElementById('spinButton'); | |
const display = document.getElementById('sequenceDisplay'); | |
const container = document.getElementById('reelsContainer'); | |
const lever = document.getElementById('lever'); | |
const proteinPanel = document.getElementById('proteinPanel'); | |
// Hide protein panel from previous spin | |
proteinPanel.classList.remove('active'); | |
// Pull the lever | |
lever.classList.add('pulled'); | |
// Check language | |
const isKorean = document.getElementById('languageToggle').checked; | |
button.disabled = true; | |
display.innerHTML = isKorean ? | |
'๐ฒ <strong>์ ์ ์ ๋ฃฐ๋ ์ ๋๋ฆฌ๋ ์ค...</strong> ๐ฒ' : | |
'๐ฒ <strong>SPINNING THE GENETIC WHEEL OF FORTUNE...</strong> ๐ฒ'; | |
container.classList.add('spinning'); | |
// Start continuous spinning immediately | |
startContinuousSpinning(); | |
// Get selected cell type and language | |
const cellType = document.querySelector('input[name="cellType"]:checked').value; | |
const language = document.getElementById('languageToggle').checked ? 'ko' : 'en'; | |
// Send request to parent window | |
window.parent.postMessage({ | |
type: 'generate_request', | |
cellType: cellType, | |
language: language | |
}, '*'); | |
} | |
// Initialize | |
initializeReels(); | |
// Language toggle event listener | |
document.getElementById('languageToggle').addEventListener('change', function() { | |
const isKorean = this.checked; | |
const initialMessage = document.getElementById('initialMessage'); | |
if (initialMessage && !isSpinning) { | |
initialMessage.textContent = isKorean ? | |
'๋ ๋ฒ๋ฅผ ๋น๊ธฐ๊ฑฐ๋ SPIN์ ๋๋ฌ ๋น์ฒจ ์ํ์ค๋ฅผ ์์ฑํ์ธ์!' : | |
'Pull the lever or press SPIN to generate your winning sequence!'; | |
} | |
}); | |
// Event listeners | |
document.getElementById('spinButton').addEventListener('click', startGeneration); | |
// Lever click functionality | |
document.getElementById('lever').addEventListener('click', function() { | |
if (!isSpinning) { | |
startGeneration(); | |
} | |
}); | |
// Listen for messages from parent window | |
window.addEventListener('message', (event) => { | |
if (event.data.type === 'sequence_generated') { | |
// Stop spinning and show the actual sequence with metadata | |
stopAndShowSequence(event.data.sequence, event.data.metadata); | |
} else if (event.data.type === 'generation_error') { | |
// Stop spinning and show error | |
reels.forEach(reel => { | |
reel.strip.classList.remove('loading'); | |
}); | |
const container = document.getElementById('reelsContainer'); | |
const display = document.getElementById('sequenceDisplay'); | |
const button = document.getElementById('spinButton'); | |
const lever = document.getElementById('lever'); | |
const isKorean = document.getElementById('languageToggle').checked; | |
container.classList.remove('spinning'); | |
display.innerHTML = `<strong style="color: #ff0000;">โ ${isKorean ? '์คํจ!' : 'BUST!'} โ</strong><br>` + event.data.error; | |
button.disabled = false; | |
isSpinning = false; | |
lever.classList.remove('pulled'); | |
updateSequenceCounter(0); | |
} | |
}); | |
// Keyboard support | |
document.addEventListener('keydown', (e) => { | |
if (e.code === 'Space' && !isSpinning) { | |
e.preventDefault(); | |
startGeneration(); | |
} | |
}); | |
// Add some casino ambiance | |
setInterval(() => { | |
if (!isSpinning && Math.random() > 0.7) { | |
const chips = document.querySelectorAll('.chip'); | |
const randomChip = chips[Math.floor(Math.random() * chips.length)]; | |
randomChip.style.animation = 'none'; | |
setTimeout(() => { | |
randomChip.style.animation = 'chipRotate 20s linear infinite'; | |
}, 10); | |
} | |
}, 5000); | |
</script> | |
</body> | |
</html> |