Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>🧠 AI Lab</title> | |
<style> | |
* { | |
box-sizing: border-box; | |
margin: 0; | |
padding: 0; | |
} | |
body { | |
font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; | |
background: linear-gradient(135deg, #111827 0%, #1f2937 50%, #111827 100%); | |
color: white; | |
min-height: 100vh; | |
padding: 1rem; | |
} | |
.container { | |
max-width: 1200px; | |
margin: 0 auto; | |
} | |
/* Main Category Menu */ | |
.main-menu { | |
min-height: 100vh; | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
text-align: center; | |
} | |
.main-header { | |
margin-bottom: 3rem; | |
} | |
.main-title { | |
font-size: 4rem; | |
font-weight: bold; | |
margin-bottom: 1rem; | |
background: linear-gradient(135deg, #06b6d4, #8b5cf6, #f59e0b); | |
-webkit-background-clip: text; | |
-webkit-text-fill-color: transparent; | |
background-clip: text; | |
} | |
.main-subtitle { | |
font-size: 1.5rem; | |
color: #d1d5db; | |
max-width: 800px; | |
} | |
.category-grid { | |
display: grid; | |
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); | |
gap: 2rem; | |
width: 100%; | |
max-width: 1000px; | |
} | |
.category-card { | |
background: #1f2937; | |
border: 3px solid #374151; | |
border-radius: 1.5rem; | |
padding: 2.5rem; | |
cursor: pointer; | |
transition: all 0.4s ease; | |
position: relative; | |
overflow: hidden; | |
} | |
.category-card:hover { | |
transform: translateY(-8px); | |
box-shadow: 0 25px 50px rgba(6, 182, 212, 0.2); | |
} | |
.category-card.fundamentals { border-color: #06b6d4; } | |
.category-card.fundamentals:hover { box-shadow: 0 25px 50px rgba(6, 182, 212, 0.3); } | |
.category-card.extras { border-color: #8b5cf6; } | |
.category-card.extras:hover { box-shadow: 0 25px 50px rgba(139, 92, 246, 0.3); } | |
.category-card.baby { border-color: #f59e0b; } | |
.category-card.baby:hover { box-shadow: 0 25px 50px rgba(245, 158, 11, 0.3); } | |
.category-card.walkthrough { border-color: #10b981; } | |
.category-card.walkthrough:hover { box-shadow: 0 25px 50px rgba(16, 185, 129, 0.3); } | |
.category-card.developer { border-color: #ef4444; } | |
.category-card.developer:hover { box-shadow: 0 25px 50px rgba(239, 68, 68, 0.3); } | |
.category-icon { | |
font-size: 4rem; | |
margin-bottom: 1.5rem; | |
} | |
.category-title { | |
font-size: 2rem; | |
font-weight: bold; | |
margin-bottom: 1rem; | |
} | |
.category-description { | |
color: #d1d5db; | |
line-height: 1.6; | |
margin-bottom: 1rem; | |
} | |
.category-count { | |
font-size: 0.875rem; | |
color: #9ca3af; | |
font-weight: 600; | |
} | |
/* Task Selection Menu */ | |
.task-selection { | |
display: none; | |
min-height: 100vh; | |
padding: 2rem 0; | |
} | |
.task-header { | |
text-align: center; | |
margin-bottom: 3rem; | |
position: relative; | |
} | |
.task-back-btn { | |
position: absolute; | |
left: 0; | |
top: 50%; | |
transform: translateY(-50%); | |
background: #374151; | |
border: none; | |
color: white; | |
padding: 0.75rem 1.5rem; | |
border-radius: 0.5rem; | |
cursor: pointer; | |
transition: background 0.3s; | |
} | |
.task-back-btn:hover { | |
background: #4b5563; | |
} | |
.task-title { | |
font-size: 2.5rem; | |
font-weight: bold; | |
margin-bottom: 0.5rem; | |
} | |
.task-subtitle { | |
font-size: 1.125rem; | |
color: #d1d5db; | |
} | |
.task-grid { | |
display: grid; | |
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); | |
gap: 1.5rem; | |
width: 100%; | |
} | |
.task-card { | |
background: #1f2937; | |
border: 2px solid #374151; | |
border-radius: 1rem; | |
padding: 2rem; | |
cursor: pointer; | |
transition: all 0.3s ease; | |
position: relative; | |
overflow: hidden; | |
} | |
.task-card:hover { | |
border-color: #06b6d4; | |
transform: translateY(-4px); | |
box-shadow: 0 20px 40px rgba(6, 182, 212, 0.15); | |
} | |
.task-icon { | |
font-size: 3rem; | |
margin-bottom: 1rem; | |
} | |
.task-name { | |
font-size: 1.5rem; | |
font-weight: bold; | |
margin-bottom: 0.5rem; | |
color: white; | |
} | |
.task-difficulty { | |
display: inline-block; | |
padding: 0.25rem 0.75rem; | |
border-radius: 1rem; | |
font-size: 0.75rem; | |
font-weight: 600; | |
margin-bottom: 1rem; | |
} | |
.difficulty-easy { background: #065f46; color: #10b981; } | |
.difficulty-medium { background: #92400e; color: #f59e0b; } | |
.difficulty-hard { background: #7c2d12; color: #ef4444; } | |
.task-description { | |
color: #d1d5db; | |
line-height: 1.6; | |
margin-bottom: 1rem; | |
} | |
.task-specs { | |
font-size: 0.875rem; | |
color: #9ca3af; | |
} | |
/* Developer Mode */ | |
.developer-form { | |
background: #1f2937; | |
border: 2px solid #374151; | |
border-radius: 1rem; | |
padding: 2rem; | |
margin-bottom: 2rem; | |
} | |
.form-group { | |
margin-bottom: 1.5rem; | |
} | |
.form-label { | |
display: block; | |
margin-bottom: 0.5rem; | |
font-weight: 600; | |
color: white; | |
} | |
.form-input, .form-select, .form-textarea { | |
width: 100%; | |
padding: 0.75rem; | |
border: 1px solid #4b5563; | |
border-radius: 0.5rem; | |
background: #111827; | |
color: white; | |
font-size: 0.875rem; | |
} | |
.form-textarea { | |
min-height: 100px; | |
resize: vertical; | |
} | |
.param-counter { | |
color: #f59e0b; | |
font-size: 0.875rem; | |
margin-top: 0.25rem; | |
} | |
.param-counter.over-limit { | |
color: #ef4444; | |
} | |
/* Training Interface */ | |
.training-interface { | |
display: none; | |
} | |
.header { | |
text-align: center; | |
margin-bottom: 2rem; | |
} | |
.header-title { | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
gap: 0.75rem; | |
margin-bottom: 1rem; | |
} | |
.back-btn { | |
position: absolute; | |
left: 0; | |
background: #374151; | |
border: none; | |
color: white; | |
padding: 0.5rem 1rem; | |
border-radius: 0.5rem; | |
cursor: pointer; | |
transition: background 0.3s; | |
} | |
.back-btn:hover { | |
background: #4b5563; | |
} | |
.brain-icon { | |
width: 2rem; | |
height: 2rem; | |
color: #06b6d4; | |
} | |
.title { | |
font-size: 1.875rem; | |
font-weight: bold; | |
color: white; | |
} | |
.subtitle { | |
color: #d1d5db; | |
max-width: 32rem; | |
margin: 0 auto; | |
} | |
.control-panel { | |
background: #1f2937; | |
border: 1px solid #374151; | |
border-radius: 0.5rem; | |
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); | |
padding: 1.5rem; | |
margin-bottom: 1.5rem; | |
text-align: center; | |
} | |
.controls { | |
display: flex; | |
flex-wrap: wrap; | |
align-items: center; | |
justify-content: center; | |
gap: 1rem; | |
} | |
.btn { | |
display: flex; | |
align-items: center; | |
gap: 0.5rem; | |
padding: 0.75rem 1.5rem; | |
border-radius: 0.5rem; | |
font-weight: 600; | |
border: none; | |
cursor: pointer; | |
transition: all 0.3s ease; | |
color: white; | |
} | |
.btn-start { | |
background: #059669; | |
box-shadow: 0 10px 15px -3px rgba(5, 150, 105, 0.25); | |
} | |
.btn-start:hover { background: #047857; } | |
.btn-pause { | |
background: #dc2626; | |
box-shadow: 0 10px 15px -3px rgba(220, 38, 38, 0.25); | |
} | |
.btn-pause:hover { background: #b91c1c; } | |
.btn-reset { | |
background: #4b5563; | |
box-shadow: 0 10px 15px -3px rgba(75, 85, 99, 0.25); | |
} | |
.btn-reset:hover { background: #374151; } | |
.stats-grid { | |
display: grid; | |
grid-template-columns: repeat(2, 1fr); | |
gap: 1rem; | |
margin-bottom: 1.5rem; | |
} | |
@media (min-width: 768px) { | |
.stats-grid { | |
grid-template-columns: repeat(4, 1fr); | |
} | |
} | |
.stat-card { | |
background: #1f2937; | |
border: 1px solid #374151; | |
border-radius: 0.5rem; | |
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); | |
padding: 1rem; | |
text-align: center; | |
} | |
.stat-value { | |
font-size: 1.5rem; | |
font-weight: bold; | |
margin-bottom: 0.25rem; | |
} | |
.stat-value.cyan { color: #06b6d4; } | |
.stat-value.purple { color: #a855f7; } | |
.stat-value.green { color: #10b981; } | |
.stat-value.orange { color: #f59e0b; } | |
.stat-label { | |
font-size: 0.875rem; | |
color: #9ca3af; | |
} | |
.main-grid { | |
display: grid; | |
gap: 1.5rem; | |
margin-bottom: 1.5rem; | |
} | |
@media (min-width: 768px) { | |
.main-grid { | |
grid-template-columns: repeat(2, 1fr); | |
} | |
} | |
.card { | |
background: #1f2937; | |
border: 1px solid #374151; | |
border-radius: 0.5rem; | |
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); | |
padding: 1.5rem; | |
} | |
.card-title { | |
font-size: 1.125rem; | |
font-weight: 600; | |
margin-bottom: 1rem; | |
color: white; | |
display: flex; | |
align-items: center; | |
gap: 0.5rem; | |
} | |
.network-canvas { | |
width: 100%; | |
height: auto; | |
border: 1px solid #4b5563; | |
border-radius: 0.5rem; | |
background: #111827; | |
} | |
.network-labels { | |
margin-top: 1rem; | |
font-size: 0.875rem; | |
color: #9ca3af; | |
display: flex; | |
justify-content: space-between; | |
} | |
.chart-container { | |
height: 16rem; | |
background: #111827; | |
border: 1px solid #4b5563; | |
border-radius: 0.5rem; | |
position: relative; | |
overflow: hidden; | |
} | |
.chart-info { | |
position: absolute; | |
bottom: 0.5rem; | |
left: 0.5rem; | |
font-size: 0.75rem; | |
color: #9ca3af; | |
background: #1f2937; | |
padding: 0.25rem 0.5rem; | |
border-radius: 0.25rem; | |
} | |
.task-grid-output { | |
display: grid; | |
grid-template-columns: repeat(2, 1fr); | |
gap: 1rem; | |
margin-top: 1.5rem; | |
} | |
@media (min-width: 768px) { | |
.task-grid-output { | |
grid-template-columns: repeat(4, 1fr); | |
} | |
} | |
.output-card { | |
padding: 1rem; | |
border-radius: 0.5rem; | |
border: 2px solid; | |
transition: all 0.3s ease; | |
text-align: center; | |
} | |
.output-card.current { | |
border-color: #06b6d4; | |
background: rgba(6, 182, 212, 0.1); | |
box-shadow: 0 10px 15px -3px rgba(6, 182, 212, 0.2); | |
} | |
.output-card.correct { | |
border-color: #10b981; | |
background: rgba(16, 185, 129, 0.1); | |
} | |
.output-card.wrong { | |
border-color: #ef4444; | |
background: rgba(239, 68, 68, 0.1); | |
} | |
.output-io { | |
font-size: 1.125rem; | |
font-family: monospace; | |
font-weight: bold; | |
color: white; | |
margin-bottom: 0.25rem; | |
} | |
.output-raw { | |
font-size: 0.875rem; | |
color: #9ca3af; | |
margin-bottom: 0.25rem; | |
} | |
.output-predicted { | |
font-size: 0.875rem; | |
font-weight: 600; | |
color: white; | |
margin-bottom: 0.25rem; | |
} | |
.output-status { | |
font-size: 0.75rem; | |
} | |
.output-status.correct { color: #10b981; } | |
.output-status.wrong { color: #ef4444; } | |
.info-section { | |
background: linear-gradient(135deg, #06b6d4 0%, #8b5cf6 100%); | |
border-radius: 0.5rem; | |
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); | |
padding: 1.5rem; | |
color: white; | |
} | |
.info-title { | |
font-size: 1.125rem; | |
font-weight: 600; | |
margin-bottom: 1rem; | |
} | |
.info-grid { | |
display: grid; | |
gap: 1rem; | |
font-size: 0.875rem; | |
} | |
@media (min-width: 768px) { | |
.info-grid { | |
grid-template-columns: repeat(3, 1fr); | |
} | |
} | |
.icon { | |
width: 1.25rem; | |
height: 1.25rem; | |
fill: currentColor; | |
} | |
.icon-stroke { | |
fill: none; | |
stroke: currentColor; | |
stroke-width: 2; | |
} | |
.data-viz { | |
width: 100%; | |
height: 300px; | |
border: 1px solid #4b5563; | |
border-radius: 0.5rem; | |
background: #111827; | |
margin-top: 1rem; | |
} | |
/* Baby Mode Styles */ | |
.baby-viz { | |
background: linear-gradient(135deg, #fef3c7, #fde68a); | |
border-radius: 1rem; | |
padding: 2rem; | |
color: #92400e; | |
margin-top: 1rem; | |
text-align: center; | |
} | |
.baby-neuron { | |
display: inline-block; | |
width: 60px; | |
height: 60px; | |
border-radius: 50%; | |
margin: 0.5rem; | |
line-height: 60px; | |
font-weight: bold; | |
font-size: 1.2rem; | |
animation: bounce 2s infinite; | |
} | |
@keyframes bounce { | |
0%, 20%, 50%, 80%, 100% { transform: translateY(0); } | |
40% { transform: translateY(-10px); } | |
60% { transform: translateY(-5px); } | |
} | |
.baby-connection { | |
stroke: #f59e0b; | |
stroke-width: 3; | |
animation: pulse 1.5s infinite; | |
} | |
@keyframes pulse { | |
0% { opacity: 0.5; } | |
50% { opacity: 1; } | |
100% { opacity: 0.5; } | |
} | |
/* Walkthrough Mode Styles */ | |
.walkthrough-overlay { | |
position: fixed; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
background: rgba(0, 0, 0, 0.8); | |
z-index: 9999; | |
display: none; | |
backdrop-filter: blur(4px); | |
} | |
.walkthrough-highlight { | |
position: absolute; | |
border: 3px solid #10b981; | |
border-radius: 0.5rem; | |
box-shadow: 0 0 0 4px rgba(16, 185, 129, 0.2), 0 0 30px rgba(16, 185, 129, 0.4); | |
pointer-events: none; | |
animation: pulse 2s infinite; | |
z-index: 10000; | |
} | |
.walkthrough-popup { | |
position: absolute; | |
background: #1f2937; | |
border: 2px solid #10b981; | |
border-radius: 1rem; | |
padding: 1.5rem; | |
max-width: 350px; | |
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.5); | |
z-index: 10001; | |
color: white; | |
} | |
.walkthrough-popup::before { | |
content: ''; | |
position: absolute; | |
width: 20px; | |
height: 20px; | |
background: #1f2937; | |
border: 2px solid #10b981; | |
transform: rotate(45deg); | |
} | |
.walkthrough-popup.top::before { | |
bottom: -12px; | |
left: 50%; | |
transform: translateX(-50%) rotate(45deg); | |
border-top: none; | |
border-left: none; | |
} | |
.walkthrough-popup.bottom::before { | |
top: -12px; | |
left: 50%; | |
transform: translateX(-50%) rotate(45deg); | |
border-bottom: none; | |
border-right: none; | |
} | |
.walkthrough-popup.left::before { | |
right: -12px; | |
top: 50%; | |
transform: translateY(-50%) rotate(45deg); | |
border-left: none; | |
border-bottom: none; | |
} | |
.walkthrough-popup.right::before { | |
left: -12px; | |
top: 50%; | |
transform: translateY(-50%) rotate(45deg); | |
border-right: none; | |
border-top: none; | |
} | |
.walkthrough-title { | |
font-size: 1.25rem; | |
font-weight: bold; | |
margin-bottom: 0.75rem; | |
color: #10b981; | |
} | |
.walkthrough-content { | |
font-size: 0.875rem; | |
line-height: 1.6; | |
color: #d1d5db; | |
margin-bottom: 1rem; | |
} | |
.walkthrough-buttons { | |
display: flex; | |
gap: 0.75rem; | |
justify-content: space-between; | |
} | |
.walkthrough-btn { | |
padding: 0.5rem 1rem; | |
border-radius: 0.5rem; | |
border: none; | |
font-weight: 600; | |
cursor: pointer; | |
transition: all 0.3s; | |
font-size: 0.875rem; | |
} | |
.walkthrough-btn-prev { | |
background: #374151; | |
color: white; | |
} | |
.walkthrough-btn-prev:hover { | |
background: #4b5563; | |
} | |
.walkthrough-btn-next { | |
background: #10b981; | |
color: white; | |
} | |
.walkthrough-btn-next:hover { | |
background: #059669; | |
} | |
.walkthrough-btn-skip { | |
background: #ef4444; | |
color: white; | |
} | |
.walkthrough-btn-skip:hover { | |
background: #dc2626; | |
} | |
.walkthrough-progress { | |
position: fixed; | |
top: 20px; | |
left: 50%; | |
transform: translateX(-50%); | |
background: #1f2937; | |
border: 2px solid #10b981; | |
border-radius: 2rem; | |
padding: 0.5rem 1.5rem; | |
color: white; | |
font-size: 0.875rem; | |
z-index: 10002; | |
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5); | |
} | |
.walkthrough-task-menu { | |
display: none; | |
padding: 2rem; | |
max-width: 800px; | |
margin: 0 auto; | |
} | |
.walkthrough-task-card { | |
background: #1f2937; | |
border: 2px solid #10b981; | |
border-radius: 1rem; | |
padding: 1.5rem; | |
margin-bottom: 1rem; | |
cursor: pointer; | |
transition: all 0.3s; | |
} | |
.walkthrough-task-card:hover { | |
transform: translateY(-4px); | |
box-shadow: 0 20px 40px rgba(16, 185, 129, 0.2); | |
} | |
.walkthrough-task-title { | |
font-size: 1.25rem; | |
font-weight: bold; | |
color: #10b981; | |
margin-bottom: 0.5rem; | |
} | |
.walkthrough-task-description { | |
color: #d1d5db; | |
font-size: 0.875rem; | |
line-height: 1.6; | |
} | |
.walkthrough-indicator { | |
position: fixed; | |
top: 10px; | |
right: 10px; | |
background: #10b981; | |
color: white; | |
padding: 0.5rem 1rem; | |
border-radius: 0.5rem; | |
font-size: 0.875rem; | |
font-weight: 600; | |
z-index: 1000; | |
display: none; | |
} | |
/* Mobile-friendly adjustments */ | |
@media (max-width: 640px) { | |
.walkthrough-popup { | |
max-width: calc(100vw - 2rem); | |
margin: 1rem; | |
} | |
.walkthrough-progress { | |
top: 10px; | |
font-size: 0.75rem; | |
padding: 0.4rem 1rem; | |
} | |
.walkthrough-title { | |
font-size: 1.125rem; | |
} | |
.walkthrough-content { | |
font-size: 0.8125rem; | |
} | |
.walkthrough-btn { | |
font-size: 0.8125rem; | |
padding: 0.4rem 0.8rem; | |
} | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<!-- Main Category Menu --> | |
<div id="mainMenu" class="main-menu"> | |
<div class="main-header"> | |
<h1 class="main-title">🧠 AI Lab</h1> | |
<p class="main-subtitle">See a variety of AI's train in real | |
time, on your browser with visuals!</p> | |
</div> | |
<div class="category-grid"> | |
<div class="category-card fundamentals" onclick="showCategory('fundamentals')"> | |
<div class="category-icon">🎯</div> | |
<h3 class="category-title">Fundamentals</h3> | |
<p class="category-description">Master the core concepts of neural networks with classic problems like logic gates and pattern recognition.</p> | |
<div class="category-count">6 Interactive Tasks</div> | |
</div> | |
<div class="category-card extras" onclick="showCategory('extras')"> | |
<div class="category-icon">🚀</div> | |
<h3 class="category-title">Extras</h3> | |
<p class="category-description">Explore advanced techniques like autoencoders, GANs, and reinforcement learning in action.</p> | |
<div class="category-count">4 Advanced Techniques</div> | |
</div> | |
<div class="category-card baby" onclick="showCategory('baby')"> | |
<div class="category-icon">🎈</div> | |
<h3 class="category-title">Baby Mode</h3> | |
<p class="category-description">Fun, colorful, and super simple explanations perfect for beginners of any age!</p> | |
<div class="category-count">5 Fun Activities</div> | |
</div> | |
<div class="category-card walkthrough" onclick="showCategory('walkthrough')"> | |
<div class="category-icon">🎓</div> | |
<h3 class="category-title">Walkthrough Mode</h3> | |
<p class="category-description">Learn step-by-step how neural networks work with interactive tutorials and explanations.</p> | |
<div class="category-count">Guided Learning</div> | |
</div> | |
<div class="category-card developer" onclick="showCategory('developer')"> | |
<div class="category-icon">⚙️</div> | |
<h3 class="category-title">Developer</h3> | |
<p class="category-description">Create your own datasets and experiments. Full control over network architecture and training.</p> | |
<div class="category-count">Custom Everything</div> | |
</div> | |
</div> | |
</div> | |
<!-- Task Selection Menus --> | |
<div id="taskSelection" class="task-selection"> | |
<div class="task-header"> | |
<button class="task-back-btn" onclick="goBackToMain()">← Back to Categories</button> | |
<h2 id="categoryTitle" class="task-title">Fundamentals</h2> | |
<p id="categorySubtitle" class="task-subtitle">Master neural network basics</p> | |
</div> | |
<div id="taskGrid" class="task-grid"> | |
<!-- Tasks will be populated by JavaScript --> | |
</div> | |
</div> | |
<!-- Developer Mode --> | |
<div id="developerMode" class="task-selection"> | |
<div class="task-header"> | |
<button class="task-back-btn" onclick="goBackToMain()">← Back to Categories</button> | |
<h2 class="task-title">Developer Mode</h2> | |
<p class="task-subtitle">Create custom AI experiments</p> | |
</div> | |
<div class="developer-form"> | |
<div class="form-group"> | |
<label class="form-label">Task Name</label> | |
<input type="text" id="devTaskName" class="form-input" placeholder="My Custom Task" value="Custom Task"> | |
</div> | |
<div class="form-group"> | |
<label class="form-label">Network Architecture (comma-separated)</label> | |
<input type="text" id="devArchitecture" class="form-input" placeholder="2,8,4,1" value="2,8,4,1"> | |
<div id="paramCount" class="param-counter">Parameters: 0 / 5000</div> | |
</div> | |
<div class="form-group"> | |
<label class="form-label">Learning Rate</label> | |
<input type="number" id="devLearningRate" class="form-input" step="0.01" min="0.01" max="1" value="0.2"> | |
</div> | |
<div class="form-group"> | |
<label class="form-label">Training Data (JSON format: [{"input": [x,y], "target": [z], "label": "text"}])</label> | |
<textarea id="devData" class="form-textarea" placeholder='[{"input": [0,0], "target": [0], "label": "0,0 → 0"}]'>[{"input": [0,0], "target": [0], "label": "0,0 → 0"},{"input": [0,1], "target": [1], "label": "0,1 → 1"},{"input": [1,0], "target": [1], "label": "1,0 → 1"},{"input": [1,1], "target": [0], "label": "1,1 → 0"}]</textarea> | |
</div> | |
<button id="createCustomTask" class="btn btn-start" onclick="createCustomTask()"> | |
<svg class="icon" fill="currentColor" viewBox="0 0 24 24"> | |
<path d="M12 5v14m-7-7h14"/> | |
</svg> | |
Create & Train | |
</button> | |
</div> | |
</div> | |
<!-- Walkthrough Mode --> | |
<div id="walkthroughMode" class="task-selection"> | |
<div class="task-header"> | |
<button class="task-back-btn" onclick="goBackToMain()">← Back to Categories</button> | |
<h2 class="task-title">Walkthrough Mode</h2> | |
<p class="task-subtitle">Learn step-by-step how neural networks work</p> | |
</div> | |
<div class="walkthrough-task-menu"> | |
<div class="walkthrough-task-card" onclick="startWalkthrough('basics')"> | |
<h3 class="walkthrough-task-title">🧠 Neural Network Basics</h3> | |
<p class="walkthrough-task-description">Learn what neurons, layers, and connections are. Understand how information flows through the network.</p> | |
</div> | |
<div class="walkthrough-task-card" onclick="startWalkthrough('training')"> | |
<h3 class="walkthrough-task-title">🎯 How Training Works</h3> | |
<p class="walkthrough-task-description">Discover how neural networks learn from data through forward propagation, loss calculation, and backpropagation.</p> | |
</div> | |
<div class="walkthrough-task-card" onclick="startWalkthrough('visualization')"> | |
<h3 class="walkthrough-task-title">📊 Understanding the Visualizations</h3> | |
<p class="walkthrough-task-description">Learn to read the network diagram, loss chart, and output predictions to understand what's happening.</p> | |
</div> | |
<div class="walkthrough-task-card" onclick="startWalkthrough('logic')"> | |
<h3 class="walkthrough-task-title">🔗 Logic Gates Tutorial</h3> | |
<p class="walkthrough-task-description">See how neural networks can learn simple AND, OR, and complex XOR logic gates step by step.</p> | |
</div> | |
</div> | |
</div> | |
<!-- Walkthrough Overlay --> | |
<div id="walkthroughOverlay" class="walkthrough-overlay"></div> | |
<div id="walkthroughHighlight" class="walkthrough-highlight" style="display: none;"></div> | |
<div id="walkthroughPopup" class="walkthrough-popup" style="display: none;"> | |
<h3 id="walkthroughTitle" class="walkthrough-title"></h3> | |
<p id="walkthroughContent" class="walkthrough-content"></p> | |
<div class="walkthrough-buttons"> | |
<button id="walkthroughPrev" class="walkthrough-btn walkthrough-btn-prev">Previous</button> | |
<button id="walkthroughNext" class="walkthrough-btn walkthrough-btn-next">Next</button> | |
<button id="walkthroughSkip" class="walkthrough-btn walkthrough-btn-skip">Exit</button> | |
</div> | |
</div> | |
<div id="walkthroughProgress" class="walkthrough-progress" style="display: none;"> | |
Step <span id="walkthroughStep">1</span> of <span id="walkthroughTotal">5</span> | |
</div> | |
<div id="walkthroughIndicator" class="walkthrough-indicator"> | |
🎓 Walkthrough Mode Active | |
</div> | |
<!-- Training Interface --> | |
<div id="trainingInterface" class="training-interface"> | |
<div class="header"> | |
<div class="header-title" style="position: relative;"> | |
<button id="backBtn" class="back-btn">← Back</button> | |
<svg class="brain-icon" fill="currentColor" viewBox="0 0 24 24"> | |
<path d="M12 2C8.5 2 6 4.5 6 8c0 1.5.5 3 1.5 4C6.5 13 6 14.5 6 16c0 3.5 2.5 6 6 6s6-2.5 6-6c0-1.5-.5-3-1.5-4 1-1 1.5-2.5 1.5-4 0-3.5-2.5-6-6-6z"/> | |
</svg> | |
<h1 id="taskTitle" class="title">AI Task Trainer</h1> | |
</div> | |
<p id="taskSubtitle" class="subtitle">Watch the neural network learn in real-time</p> | |
</div> | |
<div class="control-panel"> | |
<div class="controls"> | |
<button id="trainBtn" class="btn btn-start"> | |
<svg class="icon" fill="currentColor" viewBox="0 0 24 24"> | |
<path d="M8 5v14l11-7z"/> | |
</svg> | |
Start Training | |
</button> | |
<button id="resetBtn" class="btn btn-reset"> | |
<svg class="icon icon-stroke" viewBox="0 0 24 24"> | |
<polyline points="1 4 1 10 7 10"></polyline> | |
<path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"></path> | |
</svg> | |
Reset | |
</button> | |
</div> | |
</div> | |
<div class="stats-grid"> | |
<div class="stat-card"> | |
<div id="epochValue" class="stat-value cyan">0</div> | |
<div class="stat-label">Epochs</div> | |
</div> | |
<div class="stat-card"> | |
<div id="lossValue" class="stat-value purple">1.000000</div> | |
<div class="stat-label">Avg Loss</div> | |
</div> | |
<div class="stat-card"> | |
<div id="accuracyValue" class="stat-value green">0.0%</div> | |
<div class="stat-label">Accuracy</div> | |
</div> | |
<div class="stat-card"> | |
<div id="currentValue" class="stat-value orange">-</div> | |
<div class="stat-label">Current</div> | |
</div> | |
</div> | |
<div class="main-grid"> | |
<div class="card"> | |
<h3 class="card-title"> | |
<svg class="icon icon-stroke" viewBox="0 0 24 24"> | |
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path> | |
<circle cx="12" cy="12" r="3"></circle> | |
</svg> | |
Network Architecture | |
</h3> | |
<canvas id="networkCanvas" class="network-canvas" width="400" height="300"></canvas> | |
<div id="networkLabels" class="network-labels"> | |
<span>Input</span> | |
<span>Hidden</span> | |
<span>Output</span> | |
</div> | |
<div id="babyViz" class="baby-viz" style="display: none;"> | |
<h4>🧠 AI Brain Thinking!</h4> | |
<div id="babyNeurons"></div> | |
<p>Watch the colorful neurons light up as the AI learns!</p> | |
</div> | |
</div> | |
<div class="card"> | |
<h3 class="card-title"> | |
<svg class="icon icon-stroke" viewBox="0 0 24 24"> | |
<polyline points="23 18 13.5 8.5 8.5 13.5 1 6"></polyline> | |
<polyline points="17 18 23 18 23 12"></polyline> | |
</svg> | |
<span id="vizTitle">Training Progress</span> | |
</h3> | |
<div id="chartContainer" class="chart-container"> | |
<svg id="lossChart" width="100%" height="100%" viewBox="0 0 100 100" preserveAspectRatio="none"> | |
<defs> | |
<linearGradient id="lossGradient" x1="0%" y1="0%" x2="0%" y2="100%"> | |
<stop offset="0%" stop-color="#06b6d4" stop-opacity="0.8"/> | |
<stop offset="100%" stop-color="#06b6d4" stop-opacity="0.2"/> | |
</linearGradient> | |
</defs> | |
<polyline id="lossLine" fill="none" stroke="#06b6d4" stroke-width="0.5" points=""/> | |
<polygon id="lossArea" fill="url(#lossGradient)" points=""/> | |
</svg> | |
<canvas id="dataViz" class="data-viz" style="display: none;"></canvas> | |
<div class="chart-info"> | |
Loss: <span id="currentLoss">1.000000</span> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="card"> | |
<h3 class="card-title"> | |
<svg class="icon" fill="currentColor" viewBox="0 0 24 24"> | |
<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon> | |
</svg> | |
<span id="outputTitle">Task Output</span> | |
</h3> | |
<div class="task-grid-output" id="taskOutput"></div> | |
</div> | |
<div class="info-section"> | |
<h3 class="info-title">How it works</h3> | |
<div class="info-grid" id="infoContent"></div> | |
</div> | |
</div> | |
</div> | |
<script> | |
// Task categories | |
const CATEGORIES = { | |
fundamentals: { | |
title: 'Fundamentals', | |
subtitle: 'Master neural network basics with classic problems', | |
tasks: { | |
and: { | |
title: 'AND Gate Learning', | |
subtitle: 'Learning the AND logic gate - output 1 only when both inputs are 1', | |
architecture: [2, 4, 1], | |
learningRate: 0.3, | |
isRegression: false, | |
data: [ | |
{ input: [0, 0], target: [0], label: "0,0 → 0" }, | |
{ input: [0, 1], target: [0], label: "0,1 → 0" }, | |
{ input: [1, 0], target: [0], label: "1,0 → 0" }, | |
{ input: [1, 1], target: [1], label: "1,1 → 1" } | |
], | |
info: [ | |
"The Problem: AND gate outputs 1 only when both inputs are 1. This is linearly separable and easier to learn.", | |
"The Network: Simple 2→4→1 architecture with ReLU activation. The simplicity matches the problem complexity.", | |
"The Training: Shows how even simple networks can learn basic logic. Notice the clear decision boundary." | |
] | |
}, | |
or: { | |
title: 'OR Gate Learning', | |
subtitle: 'Learning the OR logic gate - output 1 when at least one input is 1', | |
architecture: [2, 4, 1], | |
learningRate: 0.3, | |
isRegression: false, | |
data: [ | |
{ input: [0, 0], target: [0], label: "0,0 → 0" }, | |
{ input: [0, 1], target: [1], label: "0,1 → 1" }, | |
{ input: [1, 0], target: [1], label: "1,0 → 1" }, | |
{ input: [1, 1], target: [1], label: "1,1 → 1" } | |
], | |
info: [ | |
"The Problem: OR gate outputs 1 when at least one input is 1. Also linearly separable and learns quickly.", | |
"The Network: Same 2→4→1 architecture as AND gate. Different data, same network - shows versatility.", | |
"The Training: Demonstrates how network weights adapt to different logic patterns with identical architecture." | |
] | |
}, | |
xor: { | |
title: 'XOR Gate Learning', | |
subtitle: 'Learning the XOR logic gate - the classic non-linear problem requiring hidden layers', | |
architecture: [2, 12, 8, 1], | |
learningRate: 0.3, | |
isRegression: false, | |
data: [ | |
{ input: [0, 0], target: [0], label: "0,0 → 0" }, | |
{ input: [0, 1], target: [1], label: "0,1 → 1" }, | |
{ input: [1, 0], target: [1], label: "1,0 → 1" }, | |
{ input: [1, 1], target: [0], label: "1,1 → 0" } | |
], | |
info: [ | |
"The Problem: XOR outputs 1 when inputs differ. Not linearly separable - requires multiple layers to solve.", | |
"The Network: Deeper 2→12→8→1 architecture needed. Hidden layers create complex decision boundaries.", | |
"The Training: Shows why deep learning exists - some problems need multiple layers to represent solutions." | |
] | |
}, | |
classification: { | |
title: '2D Classification', | |
subtitle: 'Learning to separate red and blue points in 2D space', | |
architecture: [2, 8, 6, 1], | |
learningRate: 0.2, | |
isRegression: false, | |
data: generateClassificationData(), | |
info: [ | |
"The Problem: Classify points as red (0) or blue (1) based on their 2D coordinates. Real-world classification example.", | |
"The Network: 2→8→6→1 architecture learns non-linear decision boundaries to separate the classes.", | |
"The Training: Visualizes how networks create decision boundaries. Each neuron contributes to the final classification." | |
], | |
hasVisualization: true | |
}, | |
sine: { | |
title: 'Sine Wave Approximation', | |
subtitle: 'Learning to approximate the sine function - regression with neural networks', | |
architecture: [1, 12, 8, 1], | |
learningRate: 0.1, | |
isRegression: true, | |
data: generateSineData(), | |
info: [ | |
"The Problem: Learn to approximate sin(x) function. Shows how networks can learn continuous functions.", | |
"The Network: 1→12→8→1 architecture with single input/output. ReLU layers approximate smooth curves.", | |
"The Training: Demonstrates function approximation capabilities. Watch the network learn the wave pattern." | |
], | |
hasVisualization: true | |
}, | |
spiral: { | |
title: 'Spiral Classification', | |
subtitle: 'Learning to classify points in a complex spiral pattern - a challenging non-linear problem', | |
architecture: [2, 16, 12, 8, 1], | |
learningRate: 0.15, | |
isRegression: false, | |
data: generateSpiralData(), | |
info: [ | |
"The Problem: Classify points in two interleaved spirals. Very complex non-linear decision boundary needed.", | |
"The Network: Deep 2→16→12→8→1 architecture required for this challenging pattern recognition task.", | |
"The Training: Shows the limits of what neural networks can learn. Complex patterns need deeper networks." | |
], | |
hasVisualization: true | |
} | |
} | |
}, | |
extras: { | |
title: 'Extra Techniques', | |
subtitle: 'Explore advanced AI methods and architectures', | |
tasks: { | |
autoencoder: { | |
title: 'Autoencoder', | |
subtitle: 'Learn to compress and reconstruct simple patterns - unsupervised learning in action', | |
architecture: [4, 2, 4], | |
learningRate: 0.3, | |
isRegression: true, | |
data: generateAutoencoderData(), | |
info: [ | |
"The Problem: Compress 4D patterns into 2D and reconstruct them perfectly. Learn efficient data representations.", | |
"The Network: 4→2→4 hourglass forces compression. Middle layer captures essential features.", | |
"The Training: Watch the network learn to encode and decode simple binary patterns." | |
] | |
}, | |
rnn: { | |
title: 'Simple RNN', | |
subtitle: 'Sequential pattern learning - predicting alternating patterns', | |
architecture: [1, 6, 1], | |
learningRate: 0.4, | |
isRegression: false, | |
data: generateSequenceData(), | |
info: [ | |
"The Problem: Learn simple alternating pattern (0→1→0→1). Foundation of sequence modeling.", | |
"The Network: 1→6→1 learns temporal dependencies. Simplified version of recurrent networks.", | |
"The Training: Shows how networks can learn to predict what comes next in sequences." | |
] | |
}, | |
gan_discriminator: { | |
title: 'GAN Discriminator', | |
subtitle: 'Learning to distinguish real vs fake data - half of a generative adversarial network', | |
architecture: [2, 6, 1], | |
learningRate: 0.3, | |
isRegression: false, | |
data: generateGANData(), | |
info: [ | |
"The Problem: Distinguish between real data (top-right) and fake data (bottom-left). Core of GANs.", | |
"The Network: 2→6→1 discriminator learns clear spatial boundaries between data types.", | |
"The Training: In real GANs, this would compete with a generator in an adversarial game." | |
], | |
hasVisualization: true | |
}, | |
transfer: { | |
title: 'Transfer Learning', | |
subtitle: 'Using pre-trained features for new tasks - efficient learning with prior knowledge', | |
architecture: [2, 8, 4, 1], | |
learningRate: 0.3, | |
isRegression: false, | |
data: generateTransferData(), | |
info: [ | |
"The Problem: Solve a new task using features learned from a previous similar task.", | |
"The Network: 2→8→4→1 where first layers are pre-trained and frozen. Only final layer learns.", | |
"The Training: Demonstrates how prior knowledge accelerates learning on related problems." | |
] | |
} | |
} | |
}, | |
baby: { | |
title: 'Baby Mode', | |
subtitle: 'Fun and simple AI learning for everyone!', | |
tasks: { | |
pet_classifier: { | |
title: '🐱🐶 Pet Classifier', | |
subtitle: 'Teach the AI to tell cats from dogs using simple features!', | |
architecture: [2, 4, 1], | |
learningRate: 0.3, | |
isRegression: false, | |
isBabyMode: true, | |
data: [ | |
{ input: [0.1, 0.9], target: [0], label: "🐱 Small ears, long tail = Cat" }, | |
{ input: [0.2, 0.8], target: [0], label: "🐱 Small ears, long tail = Cat" }, | |
{ input: [0.8, 0.2], target: [1], label: "🐶 Big ears, short tail = Dog" }, | |
{ input: [0.9, 0.1], target: [1], label: "🐶 Big ears, short tail = Dog" } | |
], | |
info: [ | |
"The Problem: Help the AI learn the difference between cats and dogs by looking at ear size and tail length!", | |
"The Network: Simple brain with just a few neurons that learn to recognize pet features.", | |
"The Fun: Watch the colorful neurons get excited when they see the right patterns!" | |
] | |
}, | |
color_mixer: { | |
title: '🎨 Color Mixer', | |
subtitle: 'Teach the AI to mix colors and create beautiful combinations!', | |
architecture: [2, 6, 3], | |
learningRate: 0.2, | |
isRegression: true, | |
isBabyMode: true, | |
data: [ | |
{ input: [1, 0], target: [1, 0, 0], label: "🔴 Red input = Red output" }, | |
{ input: [0, 1], target: [0, 0, 1], label: "🔵 Blue input = Blue output" }, | |
{ input: [1, 1], target: [0.5, 0, 0.5], label: "🟣 Red + Blue = Purple" }, | |
{ input: [0.5, 0.5], target: [0.25, 0, 0.75], label: "🟣 Mix = Light Purple" } | |
], | |
info: [ | |
"The Problem: Teach the AI how to mix colors like a real artist!", | |
"The Network: A creative brain that learns color combinations and mixing rules.", | |
"The Magic: Watch as the AI learns to create new colors by combining others!" | |
] | |
}, | |
number_guesser: { | |
title: '🔢 Number Guesser', | |
subtitle: 'The AI learns to guess if numbers are big or small!', | |
architecture: [1, 4, 1], | |
learningRate: 0.4, | |
isRegression: false, | |
isBabyMode: true, | |
data: [ | |
{ input: [0.1], target: [0], label: "0.1 = Small number 📉" }, | |
{ input: [0.2], target: [0], label: "0.2 = Small number 📉" }, | |
{ input: [0.8], target: [1], label: "0.8 = Big number 📈" }, | |
{ input: [0.9], target: [1], label: "0.9 = Big number 📈" } | |
], | |
info: [ | |
"The Problem: Help the AI learn which numbers are big and which are small!", | |
"The Network: A simple counting brain that learns about number sizes.", | |
"The Learning: Watch the AI get better at recognizing big and small numbers!" | |
] | |
}, | |
weather_predictor: { | |
title: '🌦️ Weather Predictor', | |
subtitle: 'Teach the AI to predict sunny or rainy weather!', | |
architecture: [2, 6, 1], | |
learningRate: 0.25, | |
isRegression: false, | |
isBabyMode: true, | |
data: [ | |
{ input: [0.9, 0.1], target: [1], label: "☀️ Hot + Dry = Sunny" }, | |
{ input: [0.8, 0.2], target: [1], label: "☀️ Warm + Dry = Sunny" }, | |
{ input: [0.2, 0.9], target: [0], label: "🌧️ Cool + Humid = Rainy" }, | |
{ input: [0.1, 0.8], target: [0], label: "🌧️ Cold + Humid = Rainy" } | |
], | |
info: [ | |
"The Problem: Help the AI become a weather forecaster by learning temperature and humidity patterns!", | |
"The Network: A weather brain that learns to predict rain or shine!", | |
"The Prediction: Watch as the AI learns to be a smart weather assistant!" | |
] | |
}, | |
emoji_matcher: { | |
title: '😊 Emoji Matcher', | |
subtitle: 'The AI learns to match happy and sad feelings!', | |
architecture: [2, 5, 1], | |
learningRate: 0.3, | |
isRegression: false, | |
isBabyMode: true, | |
data: [ | |
{ input: [0.9, 0.9], target: [1], label: "😊 High energy + Good mood = Happy" }, | |
{ input: [0.8, 0.8], target: [1], label: "😊 Good energy + Good mood = Happy" }, | |
{ input: [0.2, 0.2], target: [0], label: "😢 Low energy + Bad mood = Sad" }, | |
{ input: [0.1, 0.3], target: [0], label: "😢 No energy + Bad mood = Sad" } | |
], | |
info: [ | |
"The Problem: Teach the AI to understand emotions by looking at energy and mood levels!", | |
"The Network: An emotional brain that learns about feelings and happiness!", | |
"The Feelings: Watch the AI learn to recognize when someone is happy or sad!" | |
] | |
} | |
} | |
} | |
}; | |
// Data generation functions | |
function generateClassificationData() { | |
const data = []; | |
for (let i = 0; i < 8; i++) { | |
data.push({ | |
input: [Math.random() * 0.4 + 0.1, Math.random() * 0.4 + 0.5], | |
target: [0], | |
label: "Red cluster" | |
}); | |
data.push({ | |
input: [Math.random() * 0.4 + 0.5, Math.random() * 0.4 + 0.1], | |
target: [1], | |
label: "Blue cluster" | |
}); | |
} | |
return data; | |
} | |
function generateSineData() { | |
const data = []; | |
for (let i = 0; i < 20; i++) { | |
const x = (i / 19) * 2 * Math.PI; | |
data.push({ | |
input: [x / (2 * Math.PI)], | |
target: [(Math.sin(x) + 1) / 2], | |
label: `${(x/(2*Math.PI)).toFixed(2)} → ${((Math.sin(x)+1)/2).toFixed(2)}` | |
}); | |
} | |
return data; | |
} | |
function generateSpiralData() { | |
const data = []; | |
const n = 50; | |
for (let i = 0; i < n; i++) { | |
const r = i / n * 3; | |
const t = 1.75 * i / n * 2 * Math.PI; | |
data.push({ | |
input: [ | |
(r * Math.cos(t) + 1) / 2, | |
(r * Math.sin(t) + 1) / 2 | |
], | |
target: [0], | |
label: "Spiral 1" | |
}); | |
data.push({ | |
input: [ | |
(r * Math.cos(t + Math.PI) + 1) / 2, | |
(r * Math.sin(t + Math.PI) + 1) / 2 | |
], | |
target: [1], | |
label: "Spiral 2" | |
}); | |
} | |
return data; | |
} | |
function generateAutoencoderData() { | |
const data = []; | |
// Create simple, learnable patterns instead of random data | |
const patterns = [ | |
[1, 0, 0, 0], // One-hot patterns are easier to reconstruct | |
[0, 1, 0, 0], | |
[0, 0, 1, 0], | |
[0, 0, 0, 1], | |
[1, 1, 0, 0], // Simple combinations | |
[0, 0, 1, 1], | |
[1, 0, 1, 0], | |
[0, 1, 0, 1] | |
]; | |
patterns.forEach((pattern, i) => { | |
data.push({ | |
input: pattern, | |
target: pattern, // Autoencoder reconstructs input | |
label: `Pattern ${i+1}` | |
}); | |
}); | |
return data; | |
} | |
function generateSequenceData() { | |
const data = []; | |
// Much simpler pattern: just alternating 0 and 1 | |
const sequence = [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]; | |
for (let i = 0; i < sequence.length - 1; i++) { | |
data.push({ | |
input: [sequence[i]], | |
target: [sequence[i + 1]], // Predict next in sequence | |
label: `${sequence[i]} → ${sequence[i + 1]}` | |
}); | |
} | |
return data; | |
} | |
function generateGANData() { | |
const data = []; | |
// Real data: clear pattern - points in top-right quadrant | |
for (let i = 0; i < 8; i++) { | |
data.push({ | |
input: [ | |
0.7 + Math.random() * 0.3, // 0.7-1.0 range | |
0.7 + Math.random() * 0.3 // 0.7-1.0 range | |
], | |
target: [1], // Real | |
label: "Real: top-right" | |
}); | |
} | |
// Fake data: clearly different - points in bottom-left quadrant | |
for (let i = 0; i < 8; i++) { | |
data.push({ | |
input: [ | |
Math.random() * 0.3, // 0.0-0.3 range | |
Math.random() * 0.3 // 0.0-0.3 range | |
], | |
target: [0], // Fake | |
label: "Fake: bottom-left" | |
}); | |
} | |
return data; | |
} | |
function generateTransferData() { | |
const data = []; | |
// Similar to XOR but slightly different | |
data.push( | |
{ input: [0, 0], target: [1], label: "0,0 → 1 (NOT XOR)" }, | |
{ input: [0, 1], target: [0], label: "0,1 → 0 (NOT XOR)" }, | |
{ input: [1, 0], target: [0], label: "1,0 → 0 (NOT XOR)" }, | |
{ input: [1, 1], target: [1], label: "1,1 → 1 (NOT XOR)" } | |
); | |
return data; | |
} | |
// Neural Network class | |
class NeuralNetwork { | |
constructor(layers, learningRate = 0.3) { | |
this.layers = layers; | |
this.weights = []; | |
this.biases = []; | |
this.activations = []; | |
this.zValues = []; | |
this.learningRate = learningRate; | |
this.momentum = 0.8; | |
this.prevWeightUpdates = []; | |
this.prevBiasUpdates = []; | |
this.initializeWeights(); | |
} | |
initializeWeights() { | |
for (let i = 0; i < this.layers.length - 1; i++) { | |
const fanIn = this.layers[i]; | |
const fanOut = this.layers[i + 1]; | |
const limit = Math.sqrt(2 / fanIn) * 1.5; | |
this.weights[i] = Array(fanOut).fill().map(() => | |
Array(fanIn).fill().map(() => (Math.random() * 2 - 1) * limit) | |
); | |
this.biases[i] = Array(fanOut).fill().map(() => (Math.random() * 2 - 1) * 0.3); | |
this.prevWeightUpdates[i] = Array(fanOut).fill().map(() => Array(fanIn).fill(0)); | |
this.prevBiasUpdates[i] = Array(fanOut).fill(0); | |
} | |
} | |
getParameterCount() { | |
let count = 0; | |
for (let i = 0; i < this.layers.length - 1; i++) { | |
count += this.layers[i] * this.layers[i + 1]; // weights | |
count += this.layers[i + 1]; // biases | |
} | |
return count; | |
} | |
relu(x) { | |
return Math.max(0, x); | |
} | |
reluDerivative(x) { | |
return x > 0 ? 1 : 0; | |
} | |
sigmoid(x) { | |
return x > 20 ? 1 : x < -20 ? 0 : 1 / (1 + Math.exp(-x)); | |
} | |
sigmoidDerivative(x) { | |
return x * (1 - x); | |
} | |
activate(x, layer) { | |
return layer === this.layers.length - 1 ? this.sigmoid(x) : this.relu(x); | |
} | |
activateDerivative(x, layer) { | |
return layer === this.layers.length - 1 ? this.sigmoidDerivative(x) : this.reluDerivative(x); | |
} | |
forward(input) { | |
this.activations = [input]; | |
this.zValues = []; | |
for (let i = 0; i < this.weights.length; i++) { | |
const layer = []; | |
const zLayer = []; | |
for (let j = 0; j < this.weights[i].length; j++) { | |
let sum = this.biases[i][j]; | |
for (let k = 0; k < this.weights[i][j].length; k++) { | |
sum += this.weights[i][j][k] * this.activations[i][k]; | |
} | |
zLayer.push(sum); | |
layer.push(this.activate(sum, i + 1)); | |
} | |
this.zValues.push(zLayer); | |
this.activations.push(layer); | |
} | |
return this.activations[this.activations.length - 1]; | |
} | |
trainBatch(data) { | |
let totalLoss = 0; | |
const accWeightGrads = this.weights.map(layer => | |
layer.map(neuron => neuron.map(() => 0)) | |
); | |
const accBiasGrads = this.biases.map(layer => layer.map(() => 0)); | |
for (const sample of data) { | |
const output = this.forward(sample.input); | |
const loss = sample.target.reduce((sum, t, i) => | |
sum + Math.pow(t - output[i], 2), 0) / sample.target.length; | |
totalLoss += loss; | |
const errors = []; | |
const outputLayer = this.activations[this.activations.length - 1]; | |
errors[this.weights.length - 1] = outputLayer.map((o, i) => | |
(sample.target[i] - o) * this.sigmoidDerivative(o) | |
); | |
for (let i = this.weights.length - 2; i >= 0; i--) { | |
errors[i] = []; | |
for (let j = 0; j < this.layers[i + 1]; j++) { | |
let error = 0; | |
for (let k = 0; k < this.layers[i + 2]; k++) { | |
error += errors[i + 1][k] * this.weights[i + 1][k][j]; | |
} | |
const derivative = this.activateDerivative(this.zValues[i][j], i + 1); | |
errors[i][j] = error * derivative; | |
} | |
} | |
for (let i = 0; i < this.weights.length; i++) { | |
for (let j = 0; j < this.weights[i].length; j++) { | |
for (let k = 0; k < this.weights[i][j].length; k++) { | |
accWeightGrads[i][j][k] += errors[i][j] * this.activations[i][k]; | |
} | |
accBiasGrads[i][j] += errors[i][j]; | |
} | |
} | |
} | |
const weightChanges = []; | |
for (let i = 0; i < this.weights.length; i++) { | |
weightChanges[i] = []; | |
for (let j = 0; j < this.weights[i].length; j++) { | |
weightChanges[i][j] = []; | |
for (let k = 0; k < this.weights[i][j].length; k++) { | |
const avgGradient = accWeightGrads[i][j][k] / data.length; | |
const update = this.learningRate * avgGradient + this.momentum * this.prevWeightUpdates[i][j][k]; | |
this.weights[i][j][k] += update; | |
this.prevWeightUpdates[i][j][k] = update; | |
weightChanges[i][j][k] = Math.abs(avgGradient); | |
} | |
const avgBiasGradient = accBiasGrads[i][j] / data.length; | |
const biasUpdate = this.learningRate * avgBiasGradient + this.momentum * this.prevBiasUpdates[i][j]; | |
this.biases[i][j] += biasUpdate; | |
this.prevBiasUpdates[i][j] = biasUpdate; | |
} | |
} | |
const avgLoss = totalLoss / data.length; | |
return { loss: avgLoss, weightChanges }; | |
} | |
} | |
// Global state | |
let currentCategory = null; | |
let currentTask = null; | |
let network = null; | |
let isTraining = false; | |
let epoch = 0; | |
let currentLoss = 1.0; | |
let lossHistory = []; | |
let currentSample = 0; | |
let activations = []; | |
let predictions = []; | |
let weightChanges = []; | |
let avgLoss = 1.0; | |
let accuracy = 0; | |
let animationTime = 0; | |
let trainInterval = null; | |
let animationId = null; | |
// DOM elements | |
const mainMenu = document.getElementById('mainMenu'); | |
const taskSelection = document.getElementById('taskSelection'); | |
const developerMode = document.getElementById('developerMode'); | |
const trainingInterface = document.getElementById('trainingInterface'); | |
const categoryTitle = document.getElementById('categoryTitle'); | |
const categorySubtitle = document.getElementById('categorySubtitle'); | |
const taskGrid = document.getElementById('taskGrid'); | |
const backBtn = document.getElementById('backBtn'); | |
const taskTitle = document.getElementById('taskTitle'); | |
const taskSubtitle = document.getElementById('taskSubtitle'); | |
const trainBtn = document.getElementById('trainBtn'); | |
const resetBtn = document.getElementById('resetBtn'); | |
const epochValue = document.getElementById('epochValue'); | |
const lossValue = document.getElementById('lossValue'); | |
const accuracyValue = document.getElementById('accuracyValue'); | |
const currentValue = document.getElementById('currentValue'); | |
const networkCanvas = document.getElementById('networkCanvas'); | |
const networkLabels = document.getElementById('networkLabels'); | |
const lossChart = document.getElementById('lossChart'); | |
const lossLine = document.getElementById('lossLine'); | |
const lossArea = document.getElementById('lossArea'); | |
const currentLossSpan = document.getElementById('currentLoss'); | |
const taskOutput = document.getElementById('taskOutput'); | |
const outputTitle = document.getElementById('outputTitle'); | |
const infoContent = document.getElementById('infoContent'); | |
const dataViz = document.getElementById('dataViz'); | |
const chartContainer = document.getElementById('chartContainer'); | |
const vizTitle = document.getElementById('vizTitle'); | |
const babyViz = document.getElementById('babyViz'); | |
// Developer mode elements | |
const devTaskName = document.getElementById('devTaskName'); | |
const devArchitecture = document.getElementById('devArchitecture'); | |
const devLearningRate = document.getElementById('devLearningRate'); | |
const devData = document.getElementById('devData'); | |
const paramCount = document.getElementById('paramCount'); | |
// Category navigation | |
function showCategory(categoryId) { | |
window.scrollTo(0, 0); | |
currentCategory = categoryId; | |
if (categoryId === 'developer') { | |
mainMenu.style.display = 'none'; | |
developerMode.style.display = 'block'; | |
updateParameterCount(); | |
return; | |
} | |
if (categoryId === 'walkthrough') { | |
mainMenu.style.display = 'none'; | |
document.getElementById('walkthroughMode').style.display = 'block'; | |
return; | |
} | |
const category = CATEGORIES[categoryId]; | |
categoryTitle.textContent = category.title; | |
categorySubtitle.textContent = category.subtitle; | |
// Populate task grid | |
taskGrid.innerHTML = ''; | |
Object.entries(category.tasks).forEach(([taskId, task]) => { | |
const difficulty = task.architecture.length <= 3 ? 'easy' : | |
task.architecture.length <= 4 ? 'medium' : 'hard'; | |
const card = document.createElement('div'); | |
card.className = 'task-card'; | |
card.onclick = () => selectTask(taskId); | |
card.innerHTML = ` | |
<div class="task-icon">${getTaskIcon(taskId)}</div> | |
<h3 class="task-name">${task.title}</h3> | |
<span class="task-difficulty difficulty-${difficulty}">${difficulty.charAt(0).toUpperCase() + difficulty.slice(1)}</span> | |
<p class="task-description">${task.subtitle}</p> | |
<div class="task-specs">Network: ${task.architecture.join('→')} | ${task.isRegression ? 'Regression' : 'Classification'}</div> | |
`; | |
taskGrid.appendChild(card); | |
}); | |
mainMenu.style.display = 'none'; | |
taskSelection.style.display = 'block'; | |
} | |
function getTaskIcon(taskId) { | |
const icons = { | |
and: '🔗', or: '➕', xor: '⚡', classification: '🎯', | |
sine: '📈', spiral: '🌀', autoencoder: '🔄', rnn: '🔗', | |
gan_discriminator: '🎭', transfer: '📤', pet_classifier: '🐱🐶', | |
color_mixer: '🎨', number_guesser: '🔢', weather_predictor: '🌦️', | |
emoji_matcher: '😊' | |
}; | |
return icons[taskId] || '🧠'; | |
} | |
function goBackToMain() { | |
window.scrollTo(0, 0); | |
mainMenu.style.display = 'block'; | |
taskSelection.style.display = 'none'; | |
developerMode.style.display = 'none'; | |
document.getElementById('walkthroughMode').style.display = 'none'; | |
trainingInterface.style.display = 'none'; | |
currentCategory = null; | |
} | |
// Developer mode functions | |
function updateParameterCount() { | |
try { | |
const arch = devArchitecture.value.split(',').map(n => parseInt(n.trim())); | |
let params = 0; | |
for (let i = 0; i < arch.length - 1; i++) { | |
params += arch[i] * arch[i + 1] + arch[i + 1]; // weights + biases | |
} | |
paramCount.textContent = `Parameters: ${params} / 5000`; | |
paramCount.className = params > 5000 ? 'param-counter over-limit' : 'param-counter'; | |
const createBtn = document.getElementById('createCustomTask'); | |
createBtn.disabled = params > 5000; | |
} catch (e) { | |
paramCount.textContent = 'Parameters: Invalid architecture'; | |
paramCount.className = 'param-counter over-limit'; | |
} | |
} | |
function createCustomTask() { | |
window.scrollTo(0, 0); | |
try { | |
const architecture = devArchitecture.value.split(',').map(n => parseInt(n.trim())); | |
const learningRate = parseFloat(devLearningRate.value); | |
const data = JSON.parse(devData.value); | |
// Validate | |
if (architecture.some(n => isNaN(n) || n < 1)) { | |
alert('Invalid architecture. Use positive integers separated by commas.'); | |
return; | |
} | |
const params = architecture.reduce((sum, layer, i) => | |
i === 0 ? 0 : sum + architecture[i-1] * layer + layer, 0); | |
if (params > 5000) { | |
alert('Too many parameters! Keep it under 5000 to prevent crashes.'); | |
return; | |
} | |
if (!Array.isArray(data) || data.length === 0) { | |
alert('Invalid data format. Use JSON array with input, target, and label fields.'); | |
return; | |
} | |
// Create custom task | |
currentTask = { | |
title: devTaskName.value, | |
subtitle: 'Custom developer task', | |
architecture: architecture, | |
learningRate: learningRate, | |
isRegression: false, // Default to classification | |
data: data, | |
info: [ | |
`Custom Problem: ${devTaskName.value} with ${data.length} training samples.`, | |
`Custom Network: ${architecture.join('→')} architecture with ${params} parameters.`, | |
`Custom Training: Learning rate ${learningRate}, batch training with momentum.` | |
] | |
}; | |
network = new NeuralNetwork(currentTask.architecture, currentTask.learningRate); | |
// Update UI | |
taskTitle.textContent = currentTask.title; | |
taskSubtitle.textContent = currentTask.subtitle; | |
outputTitle.textContent = 'Custom Output'; | |
// Update network labels | |
const layers = currentTask.architecture; | |
let labelText = `Input (${layers[0]})`; | |
if (layers.length > 2) { | |
const hiddenSizes = layers.slice(1, -1).join('+'); | |
labelText += `||Hidden (${hiddenSizes})`; | |
} | |
labelText += `||Output (${layers[layers.length-1]})`; | |
networkLabels.innerHTML = labelText.split('||').map(l => `<span>${l}</span>`).join(''); | |
// Show training interface | |
developerMode.style.display = 'none'; | |
trainingInterface.style.display = 'block'; | |
// Initialize | |
initializeTaskOutput(); | |
updateInfoSection(); | |
reset(); | |
startAnimation(); | |
} catch (e) { | |
alert('Error creating task: ' + e.message); | |
} | |
} | |
// Task selection | |
function selectTask(taskId) { | |
window.scrollTo(0, 0); | |
currentTask = CATEGORIES[currentCategory].tasks[taskId]; | |
network = new NeuralNetwork(currentTask.architecture, currentTask.learningRate); | |
// Update UI | |
taskTitle.textContent = currentTask.title; | |
taskSubtitle.textContent = currentTask.subtitle; | |
outputTitle.textContent = currentTask.title.split(' ')[0] + ' Results'; | |
// Update network labels | |
const layers = currentTask.architecture; | |
let labelText = `Input (${layers[0]})`; | |
if (layers.length > 2) { | |
const hiddenSizes = layers.slice(1, -1).join('+'); | |
labelText += `||Hidden (${hiddenSizes})`; | |
} | |
labelText += `||Output (${layers[layers.length-1]})`; | |
networkLabels.innerHTML = labelText.split('||').map(l => `<span>${l}</span>`).join(''); | |
// Setup visualization | |
if (currentTask.hasVisualization) { | |
document.getElementById('lossChart').style.display = 'none'; | |
dataViz.style.display = 'block'; | |
vizTitle.textContent = 'Data Visualization'; | |
} else { | |
document.getElementById('lossChart').style.display = 'block'; | |
dataViz.style.display = 'none'; | |
vizTitle.textContent = 'Training Progress'; | |
} | |
// Baby mode setup | |
if (currentTask.isBabyMode) { | |
networkCanvas.style.display = 'none'; | |
babyViz.style.display = 'block'; | |
setupBabyVisualization(); | |
} else { | |
networkCanvas.style.display = 'block'; | |
babyViz.style.display = 'none'; | |
} | |
// Show interface | |
taskSelection.style.display = 'none'; | |
trainingInterface.style.display = 'block'; | |
// Initialize | |
initializeTaskOutput(); | |
updateInfoSection(); | |
reset(); | |
startAnimation(); | |
} | |
// Baby mode visualization | |
function setupBabyVisualization() { | |
const babyNeurons = document.getElementById('babyNeurons'); | |
babyNeurons.innerHTML = ''; | |
const layers = network.layers; | |
layers.forEach((layerSize, layerIndex) => { | |
for (let i = 0; i < Math.min(layerSize, 6); i++) { // Limit display | |
const neuron = document.createElement('div'); | |
neuron.className = 'baby-neuron'; | |
neuron.id = `baby-neuron-${layerIndex}-${i}`; | |
neuron.style.backgroundColor = `hsl(${layerIndex * 60 + 200}, 70%, 70%)`; | |
neuron.textContent = '😴'; | |
babyNeurons.appendChild(neuron); | |
} | |
if (layerIndex < layers.length - 1) { | |
const arrow = document.createElement('span'); | |
arrow.textContent = ' → '; | |
arrow.style.fontSize = '2rem'; | |
babyNeurons.appendChild(arrow); | |
} | |
}); | |
} | |
function updateBabyVisualization() { | |
if (!currentTask || !currentTask.isBabyMode || activations.length === 0) return; | |
const layers = network.layers; | |
layers.forEach((layerSize, layerIndex) => { | |
for (let i = 0; i < Math.min(layerSize, 6); i++) { | |
const neuron = document.getElementById(`baby-neuron-${layerIndex}-${i}`); | |
if (neuron && activations[layerIndex]) { | |
const activation = activations[layerIndex][i] || 0; | |
if (activation > 0.5) { | |
neuron.textContent = '🤩'; | |
neuron.style.backgroundColor = `hsl(${layerIndex * 60 + 200}, 90%, 80%)`; | |
} else if (activation > 0.2) { | |
neuron.textContent = '😊'; | |
neuron.style.backgroundColor = `hsl(${layerIndex * 60 + 200}, 70%, 70%)`; | |
} else { | |
neuron.textContent = '😴'; | |
neuron.style.backgroundColor = `hsl(${layerIndex * 60 + 200}, 50%, 60%)`; | |
} | |
} | |
} | |
}); | |
} | |
// Back to task selection | |
function goBack() { | |
window.scrollTo(0, 0); | |
isTraining = false; | |
clearInterval(trainInterval); | |
trainingInterface.style.display = 'none'; | |
if (currentCategory) { | |
taskSelection.style.display = 'block'; | |
} else { | |
mainMenu.style.display = 'block'; | |
} | |
currentTask = null; | |
} | |
// Initialize task output | |
function initializeTaskOutput() { | |
taskOutput.innerHTML = ''; | |
currentTask.data.forEach((data, index) => { | |
const card = document.createElement('div'); | |
card.className = 'output-card'; | |
card.id = `output-card-${index}`; | |
card.innerHTML = ` | |
<div class="output-io">${data.label}</div> | |
<div class="output-raw">Raw: <span id="raw-${index}">0.000</span></div> | |
<div class="output-predicted">Predicted: <span id="pred-${index}">-</span></div> | |
<div class="output-status" id="status-${index}">✗ Wrong</div> | |
`; | |
taskOutput.appendChild(card); | |
}); | |
} | |
// Update info section | |
function updateInfoSection() { | |
infoContent.innerHTML = currentTask.info.map(info => `<div>${info}</div>`).join(''); | |
} | |
// Draw network | |
function drawNetwork() { | |
const canvas = networkCanvas; | |
const ctx = canvas.getContext('2d'); | |
const width = canvas.width; | |
const height = canvas.height; | |
ctx.clearRect(0, 0, width, height); | |
if (activations.length === 0) return; | |
const layers = network.layers; | |
const layerSpacing = width / (layers.length + 1); | |
const nodeRadius = Math.min(15, width / 30); | |
// Draw connections | |
for (let i = 0; i < layers.length - 1; i++) { | |
for (let j = 0; j < layers[i]; j++) { | |
for (let k = 0; k < layers[i + 1]; k++) { | |
const x1 = layerSpacing * (i + 1); | |
const y1 = (height / (layers[i] + 1)) * (j + 1); | |
const x2 = layerSpacing * (i + 2); | |
const y2 = (height / (layers[i + 1] + 1)) * (k + 1); | |
const weight = network.weights[i][k][j]; | |
const intensity = Math.min(Math.abs(weight) * 1.2, 0.7); | |
const baseColor = weight > 0 ? | |
`rgba(34, 197, 94, ${intensity * 0.5})` : | |
`rgba(239, 68, 68, ${intensity * 0.5})`; | |
ctx.strokeStyle = baseColor; | |
ctx.lineWidth = Math.max(1, intensity * 1.5); | |
ctx.beginPath(); | |
ctx.moveTo(x1, y1); | |
ctx.lineTo(x2, y2); | |
ctx.stroke(); | |
// Subtle gradient flow | |
if (isTraining && Math.abs(weight) > 0.3) { | |
const flowProgress = ((animationTime * 0.5) % 120) / 120; | |
const flowX = x1 + (x2 - x1) * flowProgress; | |
const flowY = y1 + (y2 - y1) * flowProgress; | |
const flowIntensity = intensity * 0.3; | |
ctx.fillStyle = weight > 0 ? | |
`rgba(34, 197, 94, ${flowIntensity})` : | |
`rgba(239, 68, 68, ${flowIntensity})`; | |
ctx.beginPath(); | |
ctx.arc(flowX, flowY, 2, 0, 2 * Math.PI); | |
ctx.fill(); | |
} | |
} | |
} | |
} | |
// Draw nodes | |
layers.forEach((layerSize, layerIndex) => { | |
for (let nodeIndex = 0; nodeIndex < layerSize; nodeIndex++) { | |
const x = layerSpacing * (layerIndex + 1); | |
const y = (height / (layerSize + 1)) * (nodeIndex + 1); | |
const activation = activations[layerIndex] ? activations[layerIndex][nodeIndex] : 0; | |
const hue = layerIndex === 0 ? 200 : layerIndex === layers.length - 1 ? 280 : 160; | |
const saturation = 50; | |
let lightness = 35 + activation * 25; | |
if (isTraining && activation > 0.8) { | |
const pulse = Math.sin(animationTime * 0.05) * 0.1; | |
lightness += pulse * 10; | |
} | |
ctx.fillStyle = `hsl(${hue}, ${saturation}%, ${lightness}%)`; | |
ctx.beginPath(); | |
ctx.arc(x, y, nodeRadius, 0, 2 * Math.PI); | |
ctx.fill(); | |
ctx.strokeStyle = '#64748b'; | |
ctx.lineWidth = 1.5; | |
ctx.stroke(); | |
ctx.fillStyle = '#ffffff'; | |
ctx.font = `${Math.max(10, nodeRadius / 1.6)}px monospace`; | |
ctx.textAlign = 'center'; | |
ctx.textBaseline = 'middle'; | |
ctx.shadowColor = 'rgba(0, 0, 0, 0.9)'; | |
ctx.shadowBlur = 2; | |
ctx.fillText(activation.toFixed(2), x, y); | |
ctx.shadowBlur = 0; | |
} | |
}); | |
} | |
// Draw data visualization | |
function drawDataVisualization() { | |
if (!currentTask.hasVisualization) return; | |
const canvas = dataViz; | |
const ctx = canvas.getContext('2d'); | |
const width = canvas.width; | |
const height = canvas.height; | |
ctx.clearRect(0, 0, width, height); | |
if (currentTask.title.includes('Classification') || currentTask.title.includes('Spiral') || currentTask.title.includes('GAN')) { | |
// Draw classification data | |
currentTask.data.forEach((point, i) => { | |
const x = point.input[0] * width; | |
const y = height - point.input[1] * height; | |
const prediction = predictions[i]; | |
// True color | |
ctx.fillStyle = point.target[0] > 0.5 ? '#3b82f6' : '#ef4444'; | |
ctx.beginPath(); | |
ctx.arc(x, y, 6, 0, 2 * Math.PI); | |
ctx.fill(); | |
// Prediction border | |
if (prediction) { | |
ctx.strokeStyle = prediction.predicted > 0.5 ? '#3b82f6' : '#ef4444'; | |
ctx.lineWidth = prediction.correct ? 3 : 1; | |
ctx.stroke(); | |
} | |
}); | |
// For GAN discriminator, draw decision boundary area | |
if (currentTask.title.includes('GAN')) { | |
ctx.fillStyle = 'rgba(59, 130, 246, 0.1)'; // Light blue for "real" area | |
ctx.fillRect(width * 0.6, 0, width * 0.4, height * 0.4); | |
ctx.fillStyle = 'rgba(239, 68, 68, 0.1)'; // Light red for "fake" area | |
ctx.fillRect(0, height * 0.6, width * 0.4, height * 0.4); | |
} | |
} else if (currentTask.title.includes('Sine')) { | |
// Draw true sine wave | |
ctx.strokeStyle = '#64748b'; | |
ctx.lineWidth = 2; | |
ctx.beginPath(); | |
for (let x = 0; x < width; x++) { | |
const t = (x / width) * 2 * Math.PI; | |
const y = height - ((Math.sin(t) + 1) / 2) * height; | |
if (x === 0) ctx.moveTo(x, y); | |
else ctx.lineTo(x, y); | |
} | |
ctx.stroke(); | |
// Draw predictions | |
predictions.forEach((pred, i) => { | |
const x = (currentTask.data[i].input[0]) * width; | |
const y = height - pred.output * height; | |
ctx.fillStyle = pred.correct ? '#10b981' : '#ef4444'; | |
ctx.beginPath(); | |
ctx.arc(x, y, 4, 0, 2 * Math.PI); | |
ctx.fill(); | |
}); | |
} | |
} | |
// Update loss chart | |
function updateLossChart() { | |
if (lossHistory.length < 2) return; | |
const points = lossHistory.map((loss, i) => { | |
const x = (i / (lossHistory.length - 1)) * 100; | |
const y = 100 - (Math.min(loss, 1) * 90); | |
return `${x},${y}`; | |
}).join(' '); | |
lossLine.setAttribute('points', points); | |
lossArea.setAttribute('points', points + ' 100,100 0,100'); | |
} | |
// Training step | |
function trainStep() { | |
const result = network.trainBatch(currentTask.data); | |
const sample = currentTask.data[currentSample]; | |
const output = network.forward(sample.input); | |
currentLoss = result.loss; | |
activations = [...network.activations]; | |
weightChanges = result.weightChanges; | |
lossHistory.push(result.loss); | |
if (lossHistory.length > 100) lossHistory.shift(); | |
const newPredictions = currentTask.data.map(data => { | |
const output = network.forward(data.input); | |
const rawOutput = output[0]; | |
let predicted, correct; | |
if (currentTask.isRegression) { | |
// For regression, use different tolerances based on task type | |
predicted = rawOutput.toFixed(2); | |
let tolerance = 0.1; // Default tolerance | |
// Autoencoder needs more lenient accuracy since it's reconstructing multi-dimensional data | |
if (currentTask.title.includes('Autoencoder')) { | |
tolerance = 0.2; | |
// For autoencoder, check if ALL outputs are within tolerance | |
const allOutputs = network.forward(data.input); | |
let totalError = 0; | |
for (let j = 0; j < data.target.length; j++) { | |
totalError += Math.abs(allOutputs[j] - data.target[j]); | |
} | |
const avgError = totalError / data.target.length; | |
correct = avgError < tolerance; | |
} else { | |
correct = Math.abs(rawOutput - data.target[0]) < tolerance; | |
} | |
} else { | |
// For classification, use threshold | |
predicted = rawOutput >= 0.5 ? 1 : 0; | |
const target = data.target[0]; | |
correct = predicted === target; | |
} | |
return { | |
...data, | |
output: rawOutput, | |
predicted: predicted, | |
correct: correct | |
}; | |
}); | |
predictions = newPredictions; | |
const correct = newPredictions.filter(p => p.correct).length; | |
accuracy = correct / newPredictions.length; | |
const totalLoss = newPredictions.reduce((sum, p) => | |
sum + Math.pow(p.target[0] - p.output, 2), 0) / newPredictions.length; | |
avgLoss = totalLoss; | |
currentSample = (currentSample + 1) % currentTask.data.length; | |
epoch++; | |
updateUI(); | |
} | |
// Update UI | |
function updateUI() { | |
epochValue.textContent = epoch.toLocaleString(); | |
lossValue.textContent = avgLoss.toFixed(6); | |
accuracyValue.textContent = (accuracy * 100).toFixed(1) + '%'; | |
currentValue.textContent = currentTask.data[currentSample].label; | |
currentLossSpan.textContent = currentLoss.toFixed(6); | |
updateLossChart(); | |
drawNetwork(); | |
if (currentTask.hasVisualization) { | |
drawDataVisualization(); | |
} | |
if (currentTask.isBabyMode) { | |
updateBabyVisualization(); | |
} | |
// Update output cards | |
predictions.forEach((pred, index) => { | |
const card = document.getElementById(`output-card-${index}`); | |
const raw = document.getElementById(`raw-${index}`); | |
const predSpan = document.getElementById(`pred-${index}`); | |
const status = document.getElementById(`status-${index}`); | |
if (raw && predSpan && status && card) { | |
raw.textContent = pred.output.toFixed(3); | |
predSpan.textContent = pred.predicted; | |
status.textContent = pred.correct ? '✓ Correct' : '✗ Wrong'; | |
status.className = `output-status ${pred.correct ? 'correct' : 'wrong'}`; | |
card.className = `output-card ${currentSample === index ? 'current' : pred.correct ? 'correct' : 'wrong'}`; | |
} | |
}); | |
} | |
// Animation loop | |
function animate() { | |
animationTime += isTraining ? 1 : 0.2; | |
if (currentTask) { | |
drawNetwork(); | |
if (currentTask.hasVisualization) { | |
drawDataVisualization(); | |
} | |
if (currentTask.isBabyMode) { | |
updateBabyVisualization(); | |
} | |
} | |
animationId = requestAnimationFrame(animate); | |
} | |
// Start animation | |
function startAnimation() { | |
if (animationId) cancelAnimationFrame(animationId); | |
animate(); | |
} | |
// Reset function | |
function reset() { | |
if (!currentTask) return; | |
isTraining = false; | |
clearInterval(trainInterval); | |
network = new NeuralNetwork(currentTask.architecture, currentTask.learningRate); | |
epoch = 0; | |
currentLoss = 1.0; | |
lossHistory = []; | |
currentSample = 0; | |
activations = []; | |
predictions = []; | |
weightChanges = []; | |
avgLoss = 1.0; | |
accuracy = 0; | |
animationTime = 0; | |
trainBtn.innerHTML = ` | |
<svg class="icon" fill="currentColor" viewBox="0 0 24 24"> | |
<path d="M8 5v14l11-7z"/> | |
</svg> | |
Start Training | |
`; | |
trainBtn.className = 'btn btn-start'; | |
updateUI(); | |
} | |
// Event listeners | |
trainBtn.addEventListener('click', () => { | |
isTraining = !isTraining; | |
if (isTraining) { | |
trainBtn.innerHTML = ` | |
<svg class="icon" fill="currentColor" viewBox="0 0 24 24"> | |
<path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/> | |
</svg> | |
Pause Training | |
`; | |
trainBtn.className = 'btn btn-pause'; | |
// Use slower speed in walkthrough mode | |
const trainingSpeed = walkthroughActive ? walkthroughTrainingSpeed : 100; | |
trainInterval = setInterval(trainStep, trainingSpeed); | |
} else { | |
trainBtn.innerHTML = ` | |
<svg class="icon" fill="currentColor" viewBox="0 0 24 24"> | |
<path d="M8 5v14l11-7z"/> | |
</svg> | |
Start Training | |
`; | |
trainBtn.className = 'btn btn-start'; | |
clearInterval(trainInterval); | |
} | |
}); | |
resetBtn.addEventListener('click', reset); | |
backBtn.addEventListener('click', goBack); | |
// Developer mode event listeners | |
devArchitecture.addEventListener('input', updateParameterCount); | |
// Walkthrough Mode functionality | |
let walkthroughActive = false; | |
let walkthroughStep = 0; | |
let walkthroughTutorial = null; | |
let walkthroughTrainingSpeed = 500; // Slower training speed for walkthrough | |
const walkthroughTutorials = { | |
basics: { | |
title: 'Neural Network Basics', | |
steps: [ | |
{ | |
title: 'Welcome to Neural Networks!', | |
content: 'Neural networks are inspired by the human brain. They consist of layers of interconnected neurons that process information. Let\'s explore how they work!', | |
element: null, | |
position: 'center' | |
}, | |
{ | |
title: 'Input Layer', | |
content: 'This is the input layer (left side). It receives the raw data - like numbers, images, or text. Each circle represents one input neuron that holds a piece of information.', | |
element: '#networkCanvas', | |
position: 'right', | |
highlight: {x: 0, y: 0, width: 150, height: 300} | |
}, | |
{ | |
title: 'Hidden Layers', | |
content: 'These middle layers are where the "magic" happens! They transform the input data through mathematical operations, finding patterns and relationships.', | |
element: '#networkCanvas', | |
position: 'top', | |
highlight: {x: 150, y: 0, width: 100, height: 300} | |
}, | |
{ | |
title: 'Output Layer', | |
content: 'The final layer gives us the result - a prediction, classification, or decision based on what the network learned from the input.', | |
element: '#networkCanvas', | |
position: 'left', | |
highlight: {x: 250, y: 0, width: 150, height: 300} | |
}, | |
{ | |
title: 'Connections (Weights)', | |
content: 'The lines between neurons are called weights. They determine how strongly one neuron influences another. Green lines mean positive influence, red means negative.', | |
element: '#networkCanvas', | |
position: 'bottom' | |
} | |
] | |
}, | |
training: { | |
title: 'How Training Works', | |
steps: [ | |
{ | |
title: 'The Training Process', | |
content: 'Training is how neural networks learn! We show the network examples with known answers, and it adjusts its connections to get better at predicting the right answers.', | |
element: null, | |
position: 'center' | |
}, | |
{ | |
title: 'Training Controls', | |
content: 'Use these buttons to start, pause, or reset training. In walkthrough mode, training runs slower so you can see what\'s happening.', | |
element: '.control-panel', | |
position: 'bottom' | |
}, | |
{ | |
title: 'Epochs Counter', | |
content: 'An epoch is one complete pass through all training examples. More epochs usually mean better learning, but too many can cause overfitting.', | |
element: '#epochValue', | |
position: 'bottom' | |
}, | |
{ | |
title: 'Loss Value', | |
content: 'Loss measures how wrong the network\'s predictions are. Lower is better! Watch this number decrease as the network learns.', | |
element: '#lossValue', | |
position: 'bottom' | |
}, | |
{ | |
title: 'Accuracy', | |
content: 'This shows the percentage of correct predictions. 100% means the network gets every example right!', | |
element: '#accuracyValue', | |
position: 'bottom' | |
} | |
] | |
}, | |
visualization: { | |
title: 'Understanding the Visualizations', | |
steps: [ | |
{ | |
title: 'Reading the Network Diagram', | |
content: 'The network diagram shows neurons as circles. Brighter neurons are more "activated" (have higher values). Watch them light up as data flows through!', | |
element: '#networkCanvas', | |
position: 'right' | |
}, | |
{ | |
title: 'Loss Chart', | |
content: 'This chart shows the training progress over time. The line should go down as the network improves. A flat line means learning has stopped.', | |
element: '#chartContainer', | |
position: 'left' | |
}, | |
{ | |
title: 'Task Output', | |
content: 'These cards show each training example. Green means correct prediction, red means wrong. The network tries to make all cards green!', | |
element: '#taskOutput', | |
position: 'top' | |
}, | |
{ | |
title: 'Current Sample', | |
content: 'The blue highlighted card shows which example the network is currently learning from. It cycles through all examples.', | |
element: '.output-card.current', | |
position: 'top' | |
} | |
] | |
}, | |
logic: { | |
title: 'Logic Gates Tutorial', | |
steps: [ | |
{ | |
title: 'Logic Gates with Neural Networks', | |
content: 'Let\'s see how neural networks can learn logic gates - the building blocks of computers! We\'ll start with the simple AND gate.', | |
element: null, | |
position: 'center', | |
action: () => selectTask('and') | |
}, | |
{ | |
title: 'AND Gate', | |
content: 'The AND gate outputs 1 only when both inputs are 1. This is "linearly separable" - a simple line can separate the 0s from 1s.', | |
element: '#taskOutput', | |
position: 'top' | |
}, | |
{ | |
title: 'Start Training', | |
content: 'Click the Start Training button and watch the network learn! Notice how quickly it masters this simple pattern.', | |
element: '#trainBtn', | |
position: 'bottom', | |
action: () => { | |
if (!isTraining) { | |
document.getElementById('trainBtn').click(); | |
} | |
} | |
}, | |
{ | |
title: 'XOR - The Challenge', | |
content: 'Now let\'s try XOR - it outputs 1 when inputs are different. This is much harder because it\'s not linearly separable!', | |
element: null, | |
position: 'center', | |
action: () => { | |
if (isTraining) { | |
document.getElementById('trainBtn').click(); | |
} | |
goBack(); | |
selectTask('xor'); | |
} | |
}, | |
{ | |
title: 'Deeper Network Needed', | |
content: 'Notice the XOR network has more layers (2→12→8→1). The extra layers let it learn the complex pattern that XOR requires.', | |
element: '#networkCanvas', | |
position: 'right' | |
} | |
] | |
} | |
}; | |
function startWalkthrough(tutorialId) { | |
walkthroughTutorial = walkthroughTutorials[tutorialId]; | |
walkthroughStep = 0; | |
walkthroughActive = true; | |
// Show indicator | |
document.getElementById('walkthroughIndicator').style.display = 'block'; | |
// Navigate to appropriate section | |
if (tutorialId === 'logic') { | |
document.getElementById('walkthroughMode').style.display = 'none'; | |
taskSelection.style.display = 'block'; | |
currentCategory = 'fundamentals'; | |
showCategory('fundamentals'); | |
} | |
showWalkthroughStep(); | |
} | |
function showWalkthroughStep() { | |
const step = walkthroughTutorial.steps[walkthroughStep]; | |
const overlay = document.getElementById('walkthroughOverlay'); | |
const highlight = document.getElementById('walkthroughHighlight'); | |
const popup = document.getElementById('walkthroughPopup'); | |
const progress = document.getElementById('walkthroughProgress'); | |
// Update progress | |
document.getElementById('walkthroughStep').textContent = walkthroughStep + 1; | |
document.getElementById('walkthroughTotal').textContent = walkthroughTutorial.steps.length; | |
// Show overlay | |
overlay.style.display = 'block'; | |
progress.style.display = 'block'; | |
// Update popup content | |
document.getElementById('walkthroughTitle').textContent = step.title; | |
document.getElementById('walkthroughContent').textContent = step.content; | |
// Position popup and highlight | |
if (step.element) { | |
const element = document.querySelector(step.element); | |
if (element) { | |
const rect = element.getBoundingClientRect(); | |
// Highlight element | |
if (step.highlight) { | |
const canvasRect = element.getBoundingClientRect(); | |
highlight.style.left = (canvasRect.left + step.highlight.x) + 'px'; | |
highlight.style.top = (canvasRect.top + step.highlight.y) + 'px'; | |
highlight.style.width = step.highlight.width + 'px'; | |
highlight.style.height = step.highlight.height + 'px'; | |
} else { | |
highlight.style.left = rect.left - 5 + 'px'; | |
highlight.style.top = rect.top - 5 + 'px'; | |
highlight.style.width = rect.width + 10 + 'px'; | |
highlight.style.height = rect.height + 10 + 'px'; | |
} | |
highlight.style.display = 'block'; | |
// Position popup | |
positionPopup(popup, rect, step.position); | |
} | |
} else { | |
// Center popup | |
highlight.style.display = 'none'; | |
popup.style.left = '50%'; | |
popup.style.top = '50%'; | |
popup.style.transform = 'translate(-50%, -50%)'; | |
popup.className = 'walkthrough-popup'; | |
} | |
popup.style.display = 'block'; | |
// Execute action if any | |
if (step.action) { | |
setTimeout(step.action, 500); | |
} | |
// Update buttons | |
document.getElementById('walkthroughPrev').style.display = | |
walkthroughStep > 0 ? 'block' : 'none'; | |
document.getElementById('walkthroughNext').textContent = | |
walkthroughStep < walkthroughTutorial.steps.length - 1 ? 'Next' : 'Finish'; | |
} | |
function positionPopup(popup, targetRect, position) { | |
const popupRect = popup.getBoundingClientRect(); | |
let left, top; | |
switch (position) { | |
case 'top': | |
left = targetRect.left + targetRect.width / 2 - popupRect.width / 2; | |
top = targetRect.top - popupRect.height - 20; | |
popup.className = 'walkthrough-popup bottom'; | |
break; | |
case 'bottom': | |
left = targetRect.left + targetRect.width / 2 - popupRect.width / 2; | |
top = targetRect.bottom + 20; | |
popup.className = 'walkthrough-popup top'; | |
break; | |
case 'left': | |
left = targetRect.left - popupRect.width - 20; | |
top = targetRect.top + targetRect.height / 2 - popupRect.height / 2; | |
popup.className = 'walkthrough-popup right'; | |
break; | |
case 'right': | |
left = targetRect.right + 20; | |
top = targetRect.top + targetRect.height / 2 - popupRect.height / 2; | |
popup.className = 'walkthrough-popup left'; | |
break; | |
default: | |
return; | |
} | |
// Keep popup on screen | |
left = Math.max(10, Math.min(left, window.innerWidth - popupRect.width - 10)); | |
top = Math.max(10, Math.min(top, window.innerHeight - popupRect.height - 10)); | |
popup.style.left = left + 'px'; | |
popup.style.top = top + 'px'; | |
popup.style.transform = 'none'; | |
} | |
function nextWalkthroughStep() { | |
if (walkthroughStep < walkthroughTutorial.steps.length - 1) { | |
walkthroughStep++; | |
showWalkthroughStep(); | |
} else { | |
exitWalkthrough(); | |
} | |
} | |
function prevWalkthroughStep() { | |
if (walkthroughStep > 0) { | |
walkthroughStep--; | |
showWalkthroughStep(); | |
} | |
} | |
function exitWalkthrough() { | |
walkthroughActive = false; | |
document.getElementById('walkthroughOverlay').style.display = 'none'; | |
document.getElementById('walkthroughHighlight').style.display = 'none'; | |
document.getElementById('walkthroughPopup').style.display = 'none'; | |
document.getElementById('walkthroughProgress').style.display = 'none'; | |
document.getElementById('walkthroughIndicator').style.display = 'none'; | |
} | |
// Walkthrough event listeners | |
document.getElementById('walkthroughNext').addEventListener('click', nextWalkthroughStep); | |
document.getElementById('walkthroughPrev').addEventListener('click', prevWalkthroughStep); | |
document.getElementById('walkthroughSkip').addEventListener('click', exitWalkthrough); | |
// Initialize | |
startAnimation(); | |
</script> | |
<script src="walkthrough-enhancement.js"></script> | |
</body> | |
</html> |