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> |