Skip to content

Instantly share code, notes, and snippets.

@mode-mercury
Created June 12, 2025 22:52
Show Gist options
  • Select an option

  • Save mode-mercury/2e6eb26934a5a4ee4c982f91f83cc503 to your computer and use it in GitHub Desktop.

Select an option

Save mode-mercury/2e6eb26934a5a4ee4c982f91f83cc503 to your computer and use it in GitHub Desktop.
Untitled
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Neural Nexus Enhanced</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
colors: {
primary: {
400: '#7F7FFE',
500: '#5D5CDE',
600: '#4140BE'
},
secondary: {
400: '#ff8d8d',
500: '#ff6b6b',
600: '#ff4949'
},
accent: {
400: '#61edea',
500: '#32e2df',
600: '#1bc5c2'
},
neural: {
100: '#232338',
200: '#1a1a2e',
300: '#16162a',
400: '#0f0f20'
},
creative: {
400: '#FFD166',
500: '#F2994A',
600: '#E67E22'
}
},
animation: {
'gradient': 'gradient 8s ease infinite',
'float': 'float 3s ease-in-out infinite',
'pulse-slow': 'pulse 4s cubic-bezier(0.4, 0, 0.6, 1) infinite',
'bounce-slow': 'bounce 3s infinite',
'spin-slow': 'spin 8s linear infinite',
},
keyframes: {
gradient: {
'0%, 100%': {
'background-position': '0% 50%'
},
'50%': {
'background-position': '100% 50%'
}
},
float: {
'0%, 100%': {
transform: 'translateY(0)'
},
'50%': {
transform: 'translateY(-10px)'
}
}
},
dropShadow: {
'glow-sm': '0 0 4px rgba(93, 92, 222, 0.3)',
'glow': '0 0 8px rgba(93, 92, 222, 0.5)',
'glow-lg': '0 0 12px rgba(93, 92, 222, 0.7)'
}
}
}
}
</script>
<style>
/* Background gradient animation */
.bg-neural-gradient {
background: linear-gradient(-45deg, #16162a, #0f0f20, #1a1a2e, #232338);
background-size: 400% 400%;
animation: gradient 15s ease infinite;
}
@keyframes gradient {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
/* Glow effect */
.glow {
box-shadow: 0 0 15px rgba(93, 92, 222, 0.5);
transition: all 0.3s ease;
}
.glow:hover {
box-shadow: 0 0 25px rgba(93, 92, 222, 0.7);
}
.glow-text {
text-shadow: 0 0 10px rgba(93, 92, 222, 0.7);
}
/* Enhanced pulse animation for nodes with glow */
@keyframes pulse-node {
0% {
r: 4;
opacity: 0.7;
filter: drop-shadow(0 0 1px rgba(93, 92, 222, 0.3));
}
50% {
r: 7;
opacity: 1;
filter: drop-shadow(0 0 6px rgba(93, 92, 222, 0.7));
}
100% {
r: 4;
opacity: 0.7;
filter: drop-shadow(0 0 1px rgba(93, 92, 222, 0.3));
}
}
@keyframes pulse-node-accent {
0% {
r: 4;
opacity: 0.7;
filter: drop-shadow(0 0 1px rgba(50, 226, 223, 0.3));
}
50% {
r: 7;
opacity: 1;
filter: drop-shadow(0 0 6px rgba(50, 226, 223, 0.7));
}
100% {
r: 4;
opacity: 0.7;
filter: drop-shadow(0 0 1px rgba(50, 226, 223, 0.3));
}
}
@keyframes pulse-node-secondary {
0% {
r: 4;
opacity: 0.7;
filter: drop-shadow(0 0 1px rgba(255, 107, 107, 0.3));
}
50% {
r: 7;
opacity: 1;
filter: drop-shadow(0 0 6px rgba(255, 107, 107, 0.7));
}
100% {
r: 4;
opacity: 0.7;
filter: drop-shadow(0 0 1px rgba(255, 107, 107, 0.3));
}
}
.pulse-animation {
animation: pulse-node 2s infinite;
}
.pulse-animation-accent {
animation: pulse-node-accent 2s infinite;
}
.pulse-animation-secondary {
animation: pulse-node-secondary 2s infinite;
}
/* Enhanced flow animation for connections with glow */
@keyframes flow-through {
0% {
stroke-dashoffset: 50;
stroke-width: 1;
}
50% {
stroke-width: 2;
}
100% {
stroke-dashoffset: 0;
stroke-width: 1;
}
}
.flow-animation {
stroke-dasharray: 5, 3;
animation: flow-through 1.5s linear infinite;
filter: drop-shadow(0 0 2px rgba(93, 92, 222, 0.6));
}
/* Data packet animation */
@keyframes data-packet-flow {
0% {
offset-distance: 0%;
opacity: 0;
}
10% {
opacity: 1;
}
90% {
opacity: 1;
}
100% {
offset-distance: 100%;
opacity: 0;
}
}
.data-packet {
offset-path: path(var(--path));
animation: data-packet-flow 1.5s ease-in-out;
animation-fill-mode: forwards;
}
/* Fade in animation */
@keyframes fade-in {
0% {
opacity: 0;
transform: translateY(10px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.fade-in {
animation: fade-in 0.5s ease-in;
}
/* Enhanced scrollbar for dark mode */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: rgba(26, 26, 46, 0.6);
border-radius: 8px;
}
::-webkit-scrollbar-thumb {
background: linear-gradient(to bottom, #5D5CDE, #32e2df);
border-radius: 8px;
}
::-webkit-scrollbar-thumb:hover {
background: linear-gradient(to bottom, #7F7FFE, #61edea);
}
/* Glass effect */
.glass {
background: rgba(26, 26, 46, 0.2);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
border: 1px solid rgba(255, 255, 255, 0.05);
}
.glass-dark {
background: rgba(15, 15, 32, 0.7);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
border: 1px solid rgba(255, 255, 255, 0.05);
}
/* Token animation */
@keyframes token-pulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.05);
}
100% {
transform: scale(1);
}
}
.token-animate {
animation: token-pulse 2s infinite;
}
/* Custom button shine effect */
.btn-shine {
position: relative;
overflow: hidden;
}
.btn-shine::after {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(to bottom right,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.1) 50%,
rgba(255, 255, 255, 0) 100%);
transform: rotate(45deg);
animation: shine 3s infinite;
}
@keyframes shine {
0% {
transform: translateX(-100%) rotate(45deg);
}
20%,
100% {
transform: translateX(100%) rotate(45deg);
}
}
/* Voice wave animation */
.voice-wave {
position: relative;
}
.voice-wave span {
position: absolute;
width: 6px;
height: 6px;
background: currentColor;
border-radius: 50%;
animation: voice-wave-anim 1.5s ease-in-out infinite;
}
.voice-wave span:nth-child(1) {
left: -12px;
animation-delay: 0s;
}
.voice-wave span:nth-child(2) {
left: -6px;
animation-delay: 0.2s;
}
.voice-wave span:nth-child(3) {
left: 0;
animation-delay: 0.4s;
}
.voice-wave span:nth-child(4) {
left: 6px;
animation-delay: 0.6s;
}
.voice-wave span:nth-child(5) {
left: 12px;
animation-delay: 0.8s;
}
@keyframes voice-wave-anim {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(-8px);
}
}
/* Audio visualization */
.audio-bar {
display: inline-block;
width: 3px;
margin: 0 1px;
background: linear-gradient(to top, #32e2df, #5D5CDE);
border-radius: 1px;
transition: height 0.1s ease;
}
/* Image generator */
.color-swatch {
width: 24px;
height: 24px;
border-radius: 4px;
display: inline-block;
cursor: pointer;
transition: transform 0.2s ease;
border: 2px solid transparent;
}
.color-swatch:hover {
transform: scale(1.1);
}
.color-swatch.active {
border: 2px solid white;
transform: scale(1.1);
}
/* Tooltip styles */
.tooltip {
position: relative;
display: inline-block;
cursor: help;
}
.tooltip .tooltiptext {
visibility: hidden;
width: 200px;
background: rgba(15, 15, 32, 0.9);
backdrop-filter: blur(8px);
color: #fff;
text-align: center;
border-radius: 6px;
padding: 8px;
position: absolute;
z-index: 1;
bottom: 125%;
left: 50%;
transform: translateX(-50%);
opacity: 0;
transition: opacity 0.3s;
font-size: 0.75rem;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.tooltip .tooltiptext::after {
content: "";
position: absolute;
top: 100%;
left: 50%;
margin-left: -5px;
border-width: 5px;
border-style: solid;
border-color: rgba(15, 15, 32, 0.9) transparent transparent transparent;
}
.tooltip:hover .tooltiptext {
visibility: visible;
opacity: 1;
}
/* Help section */
.help-step {
transition: all 0.3s ease;
cursor: pointer;
}
.help-step:hover {
background: rgba(93, 92, 222, 0.1);
}
.help-step.active {
background: rgba(93, 92, 222, 0.15);
border-left: 3px solid #5D5CDE;
}
</style>
</head>
<body class="bg-neural-gradient text-gray-200 min-h-screen transition-colors duration-300">
<div class="absolute inset-0 overflow-hidden pointer-events-none opacity-30">
<div class="absolute w-96 h-96 bg-primary-500 rounded-full filter blur-3xl -top-20 -left-20 animate-pulse-slow"></div>
<div class="absolute w-96 h-96 bg-accent-500 rounded-full filter blur-3xl -bottom-20 -right-20 animate-pulse-slow" style="animation-delay: 2s"></div>
</div>
<!-- Help button (fixed position) -->
<div class="fixed top-4 right-4 z-20">
<button id="help-toggle" class="w-10 h-10 rounded-full bg-gradient-to-r from-accent-500 to-primary-500 text-white flex items-center justify-center shadow-lg hover:shadow-xl transition-all">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</button>
</div>
<!-- Help overlay (hidden by default) -->
<div id="help-overlay" class="fixed inset-0 z-30 bg-neural-400/80 backdrop-blur-sm flex items-center justify-center hidden">
<div class="w-full max-w-3xl max-h-[90vh] overflow-auto bg-neural-200 rounded-xl shadow-2xl p-6 border border-primary-500/30">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-bold text-white">Neural Nexus - Interactive Guide</h2>
<button id="help-close" class="text-gray-400 hover:text-white">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div class="grid grid-cols-3 gap-6">
<!-- Help navigation sidebar -->
<div class="col-span-1 border-r border-gray-700 pr-4">
<div class="text-sm font-medium text-gray-400 mb-2">GUIDE SECTIONS</div>
<div class="flex flex-col space-y-1">
<div class="help-step active p-2 rounded" data-step="neural-network">
Neural Network Visualization
</div>
<div class="help-step p-2 rounded" data-step="language-model">
How the Language Model Works
</div>
<div class="help-step p-2 rounded" data-step="voice-features">
Voice Features Guide
</div>
<div class="help-step p-2 rounded" data-step="image-generator">
AI Image Generation
</div>
<div class="help-step p-2 rounded" data-step="music-generator">
AI Music Creation
</div>
<div class="help-step p-2 rounded" data-step="training-data">
Training & Data Sources
</div>
<div class="help-step p-2 rounded" data-step="tips-tricks">
Tips & Tricks
</div>
</div>
</div>
<!-- Help content area -->
<div class="col-span-2">
<!-- Neural Network section -->
<div id="help-neural-network" class="help-content">
<h3 class="text-lg font-semibold text-accent-400 mb-3">Understanding the Neural Network Visualization</h3>
<p class="mb-4">
The visualization shows a simple neural network with three layers: Input, Hidden, and Output.
This represents how AI models process information through interconnected nodes.
</p>
<div class="mb-4 p-3 bg-neural-300 rounded-lg">
<h4 class="font-medium text-primary-400 mb-2">What You're Seeing:</h4>
<ul class="list-disc list-inside text-sm space-y-2">
<li><span class="text-primary-400 font-medium">Blue nodes (Input layer)</span>: Represent the words from your input text</li>
<li><span class="text-accent-400 font-medium">Teal nodes (Hidden layer)</span>: Process and transform the input data</li>
<li><span class="text-secondary-400 font-medium">Red nodes (Output layer)</span>: Generate potential next words</li>
<li>The <span class="text-white font-medium">glowing connections</span> show information flowing between nodes</li>
<li>The <span class="text-white font-medium">moving dots</span> represent data flowing through the network</li>
</ul>
</div>
<p class="mb-4">
When you enter text, the system activates different patterns in the network.
Each step in generating a response activates different pathways, showing how an AI
language model might process text and make predictions about what comes next.
</p>
<div class="p-3 bg-neural-300 rounded-lg text-sm">
<p class="font-medium text-accent-400">Note:</p>
<p>This is a simplified visualization - real neural networks can have millions or billions of parameters
across many more layers, making them impossible to visualize completely.</p>
</div>
</div>
<!-- Language Model section -->
<div id="help-language-model" class="help-content hidden">
<h3 class="text-lg font-semibold text-accent-400 mb-3">How the Language Model Works</h3>
<p class="mb-4">
Our app uses a simple n-gram language model, which predicts the next word based on the
previous 1-2 words (context). Real AI language models like GPT-4 and Claude use much more sophisticated
transformer architectures with attention mechanisms.
</p>
<div class="mb-4 p-3 bg-neural-300 rounded-lg">
<h4 class="font-medium text-primary-400 mb-2">Generation Process:</h4>
<ol class="list-decimal list-inside text-sm space-y-2">
<li>The model takes your input text</li>
<li>It tokenizes the text (breaks it into words or subwords)</li>
<li>For each position, it considers the context (previous words)</li>
<li>It uses statistical patterns from training data to predict possible next words</li>
<li>It selects one of these candidates (our model chooses randomly)</li>
<li>This process repeats until the response is complete</li>
</ol>
</div>
<p class="mb-3">
The "Model State" section shows you which patterns the model is using to generate text:
</p>
<div class="p-3 bg-neural-300 rounded-lg text-sm mb-4">
<ul class="list-disc list-inside space-y-1">
<li><span class="text-primary-400 font-medium">Using 2-word context</span>: The model is using the previous two words (trigram)</li>
<li><span class="text-accent-400 font-medium">Using 1-word context</span>: The model is using just the previous word (bigram)</li>
<li><span class="text-secondary-400 font-medium">Using random selection</span>: No matching patterns were found, so it's selecting randomly</li>
</ul>
</div>
<p>
You can improve the model's responses by training it with more data using the "Train" tab!
</p>
</div>
<!-- Voice Features section -->
<div id="help-voice-features" class="help-content hidden">
<h3 class="text-lg font-semibold text-accent-400 mb-3">Using Voice Features</h3>
<div class="grid grid-cols-2 gap-4 mb-4">
<div class="p-3 bg-neural-300 rounded-lg">
<h4 class="font-medium text-primary-400 mb-2">Voice Input (Dictation)</h4>
<p class="text-sm mb-3">
Speak to the app instead of typing with the built-in speech recognition.
</p>
<ol class="list-decimal list-inside text-xs space-y-1">
<li>Click the microphone icon in the input field</li>
<li>When the sound wave appears, start speaking</li>
<li>The app will transcribe your speech in real-time</li>
<li>When you stop speaking, it automatically processes your request</li>
</ol>
</div>
<div class="p-3 bg-neural-300 rounded-lg">
<h4 class="font-medium text-accent-400 mb-2">Voice Output (Text-to-Speech)</h4>
<p class="text-sm mb-3">
Have the app read responses aloud using speech synthesis.
</p>
<ol class="list-decimal list-inside text-xs space-y-1">
<li>Responses are read automatically when voice is enabled</li>
<li>Click the speaker icon next to "Output" for on-demand speech</li>
<li>Change the voice in the Voice Settings panel</li>
<li>Adjust speech speed using the slider control</li>
</ol>
</div>
</div>
<div class="p-3 bg-neural-300 rounded-lg text-sm">
<h4 class="font-medium text-primary-400 mb-2">Voice Controls:</h4>
<ul class="list-disc list-inside space-y-1">
<li>Toggle voice output on/off with the Voice button</li>
<li>Select from available system voices in the dropdown</li>
<li>Adjust the speaking rate from 0.5x (slow) to 2.0x (fast)</li>
<li>Click the microphone again to stop dictation</li>
</ul>
<p class="mt-3 text-accent-400">
<strong>Note:</strong> Voice features depend on browser support and may work differently across devices.
</p>
</div>
</div>
<!-- Image Generator section -->
<div id="help-image-generator" class="help-content hidden">
<h3 class="text-lg font-semibold text-accent-400 mb-3">Using the AI Image Generator</h3>
<p class="mb-4">
The image generator creates abstract algorithmic art that simulates AI-generated imagery.
Real AI image generators like DALL-E and Midjourney use diffusion models trained on millions of images.
</p>
<div class="mb-4 p-3 bg-neural-300 rounded-lg">
<h4 class="font-medium text-primary-400 mb-2">How to Generate Images:</h4>
<ol class="list-decimal list-inside text-sm space-y-2">
<li>Go to the "Create" tab and select "Image"</li>
<li>Enter a descriptive prompt (colors, shapes, style)</li>
<li>Adjust the complexity slider to control detail level</li>
<li>Choose a color palette that matches your vision</li>
<li>Click "Generate" to create your image</li>
<li>Save your creation using the download button</li>
</ol>
</div>
<p class="mb-4">
Each generation creates a unique image based on your inputs and randomness.
The results are abstract patterns that might remind you of AI-generated art.
</p>
<div class="p-3 bg-neural-300 rounded-lg text-sm">
<h4 class="font-medium text-accent-400">Tips for Better Results:</h4>
<ul class="list-disc list-inside space-y-1">
<li>Use descriptive terms like "waves," "geometric," or "organic"</li>
<li>Higher complexity creates more detailed patterns</li>
<li>The seed value lets you recreate the same image again</li>
<li>Try different color palettes for dramatic changes</li>
<li>You can combine multiple techniques like "spirals with fractals"</li>
</ul>
</div>
</div>
<!-- Music Generator section -->
<div id="help-music-generator" class="help-content hidden">
<h3 class="text-lg font-semibold text-accent-400 mb-3">Creating Music with the AI Composer</h3>
<p class="mb-4">
The music generator creates simple melodic patterns using the Web Audio API.
This simulates how AI music generators work, though real AI music tools like
MusicLM and AudioLDM use much more sophisticated neural networks.
</p>
<div class="mb-4 p-3 bg-neural-300 rounded-lg">
<h4 class="font-medium text-primary-400 mb-2">How to Create Music:</h4>
<ol class="list-decimal list-inside text-sm space-y-2">
<li>Go to the "Create" tab and select "Music"</li>
<li>Choose a musical scale (major, minor, pentatonic)</li>
<li>Set the tempo (speed) of your composition</li>
<li>Adjust the complexity slider for pattern variations</li>
<li>Select an instrument type</li>
<li>Click "Generate" to create a melody</li>
<li>Press "Play" to hear your composition</li>
</ol>
</div>
<div class="p-3 bg-neural-300 rounded-lg text-sm">
<h4 class="font-medium text-accent-400">Understanding the Visualizer:</h4>
<p class="mb-2">
The audio waveform shows you the volume and pattern of the generated music:
</p>
<ul class="list-disc list-inside space-y-1">
<li>Taller bars represent louder notes</li>
<li>The pattern shows you the rhythm and structure</li>
<li>Colors represent different frequencies (pitches)</li>
<li>The sequencer grid shows which notes are playing</li>
</ul>
</div>
</div>
<!-- Training Data section -->
<div id="help-training-data" class="help-content hidden">
<h3 class="text-lg font-semibold text-accent-400 mb-3">Training & Data Sources</h3>
<p class="mb-4">
The language model learns from the text you provide. The more quality data you give it,
the better its responses will become. You can add data in several ways:
</p>
<div class="mb-4 p-3 bg-neural-300 rounded-lg">
<h4 class="font-medium text-primary-400 mb-2">Adding Training Data:</h4>
<ul class="list-disc list-inside text-sm space-y-2">
<li>
<span class="text-accent-400 font-medium">Train tab</span>:
Directly enter text to add to the model's knowledge
</li>
<li>
<span class="text-accent-400 font-medium">Web tab</span>:
Import content from websites (enter URL and fetch)
</li>
<li>
<span class="text-accent-400 font-medium">File tab</span>:
Upload text files (.txt, .md) to train on larger documents
</li>
<li>
<span class="text-accent-400 font-medium">Import/Export</span>:
Save trained models and share them with others
</li>
</ul>
</div>
<div class="mb-4">
<h4 class="font-medium text-primary-400 mb-2">How Training Works:</h4>
<p class="text-sm">
The system analyzes patterns in your text, looking at sequences of words (n-grams).
It learns which words tend to follow others, building a statistical model of language.
These patterns are stored and used to generate new text that follows similar patterns.
</p>
</div>
<div class="p-3 bg-neural-300 rounded-lg text-sm">
<h4 class="font-medium text-accent-400 mb-2">Tips for Better Training:</h4>
<ul class="list-disc list-inside space-y-1">
<li>Use complete, grammatically correct sentences</li>
<li>Train on text in a similar style to what you want to generate</li>
<li>More data usually leads to better results</li>
<li>Diverse sources help the model learn different patterns</li>
<li>Check the "Sources" tab to see what data the model has learned from</li>
<li>Use the Export feature to save your trained model</li>
</ul>
</div>
</div>
<!-- Tips & Tricks section -->
<div id="help-tips-tricks" class="help-content hidden">
<h3 class="text-lg font-semibold text-accent-400 mb-3">Tips & Tricks</h3>
<div class="grid grid-cols-2 gap-4 mb-4">
<div class="p-3 bg-neural-300 rounded-lg">
<h4 class="font-medium text-primary-400 mb-2">For Better Text Generation</h4>
<ul class="list-disc list-inside text-xs space-y-1">
<li>Start prompts with relevant context words</li>
<li>Train on specialized text for domain-specific responses</li>
<li>Generate multiple times for different variations</li>
<li>More training data = better quality responses</li>
<li>Import pre-trained models for specialized topics</li>
</ul>
</div>
<div class="p-3 bg-neural-300 rounded-lg">
<h4 class="font-medium text-primary-400 mb-2">For Better Voice Interaction</h4>
<ul class="list-disc list-inside text-xs space-y-1">
<li>Speak clearly and at a moderate pace</li>
<li>Use a quiet environment for better recognition</li>
<li>Try different voices to find one you prefer</li>
<li>For longer texts, adjust the speech rate faster</li>
<li>Click the microphone again to cancel dictation</li>
</ul>
</div>
</div>
<div class="grid grid-cols-2 gap-4 mb-4">
<div class="p-3 bg-neural-300 rounded-lg">
<h4 class="font-medium text-primary-400 mb-2">For Creative Images</h4>
<ul class="list-disc list-inside text-xs space-y-1">
<li>Mix different style terms for unique results</li>
<li>Save the seed number to recreate favorites</li>
<li>Try "evolving" an image with small changes</li>
<li>Combine contrasting techniques (e.g., "geometric organic")</li>
<li>Custom color palettes create distinctive moods</li>
</ul>
</div>
<div class="p-3 bg-neural-300 rounded-lg">
<h4 class="font-medium text-primary-400 mb-2">For Interesting Music</h4>
<ul class="list-disc list-inside text-xs space-y-1">
<li>Minor scales create moodier compositions</li>
<li>Pentatonic scales sound good at any complexity</li>
<li>Higher complexity creates more varied melodies</li>
<li>Slower tempos work better for complex patterns</li>
<li>Try layering different instrument sounds</li>
</ul>
</div>
</div>
<div class="p-3 bg-neural-300 rounded-lg">
<h4 class="font-medium text-accent-400 mb-2">Keyboard Shortcuts:</h4>
<div class="grid grid-cols-2 gap-2 text-xs">
<div><span class="bg-neural-100 px-1 rounded">Enter</span> - Submit prompt</div>
<div><span class="bg-neural-100 px-1 rounded">Esc</span> - Close help panel</div>
<div><span class="bg-neural-100 px-1 rounded">Ctrl+M</span> - Toggle microphone</div>
<div><span class="bg-neural-100 px-1 rounded">Space</span> - Play/pause music</div>
<div><span class="bg-neural-100 px-1 rounded">Ctrl+S</span> - Save image/music</div>
<div><span class="bg-neural-100 px-1 rounded">?</span> - Open this help panel</div>
</div>
</div>
</div>
<!-- Navigation buttons -->
<div class="mt-6 flex justify-between">
<button id="prev-help" class="px-3 py-1.5 bg-neural-300 text-white rounded-lg text-sm hover:bg-neural-100 transition-colors">
← Previous
</button>
<button id="next-help" class="px-3 py-1.5 bg-gradient-to-r from-primary-500 to-accent-500 text-white rounded-lg text-sm hover:from-primary-600 hover:to-accent-600 transition-colors">
Next →
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Main content container -->
<div class="container mx-auto px-3 py-6 max-w-md relative z-10">
<h1 class="text-2xl font-bold text-center mb-5 text-transparent bg-clip-text bg-gradient-to-r from-primary-400 to-accent-400 drop-shadow-glow">Neural Nexus Enhanced</h1>
<!-- Visualization area (always visible) -->
<div class="mb-6 glass rounded-xl p-4 shadow-lg border border-gray-700 glow relative overflow-hidden">
<div class="flex justify-between items-center mb-3">
<h2 class="text-lg font-semibold text-white drop-shadow-glow-sm">Neural Network</h2>
<div class="flex items-center gap-2">
<button id="export-btn" class="text-xs bg-gradient-to-r from-primary-500 to-primary-600 text-white px-3 py-1.5 rounded-lg shadow-md btn-shine" title="Export model data">
Export
</button>
<label for="import-file" class="text-xs bg-gradient-to-r from-accent-500 to-accent-600 text-white px-3 py-1.5 rounded-lg shadow-md cursor-pointer btn-shine" title="Import model data">
Import
<input id="import-file" type="file" class="hidden" accept=".json">
</label>
</div>
</div>
<!-- Neural Network Visualization -->
<div id="network-viz" class="h-[170px] w-full rounded-lg glass-dark p-3 mb-3 relative">
<svg id="neural-network" class="w-full h-full"></svg>
<div class="absolute top-2 right-2">
<div class="tooltip">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-gray-400 hover:text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="tooltiptext">
This visualization shows how neural networks process data. Input nodes (left) receive your text, hidden nodes (middle) process it, and output nodes (right) generate responses.
</span>
</div>
</div>
</div>
<!-- Input/Output Display -->
<div class="grid grid-cols-2 gap-3">
<div>
<h3 class="text-xs font-medium mb-1 flex items-center">
<span class="inline-block w-2 h-2 rounded-full bg-primary-400 mr-1.5"></span>
Input
<div class="tooltip ml-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 text-gray-400 hover:text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="tooltiptext">
Your text is broken into tokens (words) that the model processes.
</span>
</div>
</h3>
<div id="input-viz" class="h-[80px] overflow-auto w-full glass-dark rounded-lg p-2 text-xs">
<p class="text-gray-400 text-center">Enter input...</p>
</div>
</div>
<div>
<h3 class="text-xs font-medium mb-1 flex items-center">
<span class="inline-block w-2 h-2 rounded-full bg-accent-400 mr-1.5"></span>
Output
<button id="speak-output" class="ml-2 p-1 text-accent-400 hover:text-white" title="Speak output">
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.536 8.464a5 5 0 010 7.072m2.828-9.9a9 9 0 010 12.728M5.586 15H4a1 1 0 01-1-1v-4a1 1 0 011-1h1.586l4.707-4.707C10.923 3.663 12 4.109 12 5v14c0 .891-1.077 1.337-1.707.707L5.586 15z" />
</svg>
</button>
<div class="tooltip ml-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 text-gray-400 hover:text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="tooltiptext">
Generated text appears here word by word as the model creates it.
</span>
</div>
</h3>
<div id="output-viz" class="h-[80px] overflow-auto w-full glass-dark rounded-lg p-2 text-xs">
<p class="text-gray-400 text-center">Response will appear here</p>
</div>
</div>
</div>
<!-- Model State -->
<div class="mt-3">
<h3 class="text-xs font-medium mb-1 flex items-center">
<span class="inline-block w-2 h-2 rounded-full bg-secondary-400 mr-1.5"></span>
Model State
<div class="tooltip ml-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 text-gray-400 hover:text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="tooltiptext">
Shows the model's internal patterns and decision-making process during text generation.
</span>
</div>
</h3>
<div id="model-state" class="text-xs h-[60px] overflow-auto glass-dark rounded-lg p-2"></div>
</div>
</div>
<!-- Speech settings panel -->
<div class="mb-4 glass rounded-xl p-3 shadow-lg border border-gray-700">
<div class="flex justify-between items-center mb-2">
<h3 class="text-sm font-medium text-white">Voice Settings</h3>
<div class="flex space-x-2">
<button id="toggle-voice" class="text-xs bg-gradient-to-r from-accent-500 to-accent-600 text-white px-2 py-1 rounded-lg shadow-md btn-shine" title="Toggle voice output">
<span id="voice-status">Voice: ON</span>
</button>
</div>
</div>
<div class="grid grid-cols-2 gap-3">
<div>
<label for="voice-select" class="text-xs text-gray-300">Voice</label>
<select id="voice-select" class="w-full text-xs rounded bg-neural-100 text-white border border-gray-700 focus:outline-none focus:ring-1 focus:ring-accent-500">
<option value="">Loading voices...</option>
</select>
</div>
<div>
<label for="voice-rate" class="text-xs text-gray-300">Speed</label>
<div class="flex items-center space-x-2">
<input type="range" id="voice-rate" min="0.5" max="2" step="0.1" value="1" class="w-full h-1 accent-accent-500">
<span id="rate-value" class="text-xs">1.0</span>
</div>
</div>
</div>
</div>
<!-- Tabs navigation -->
<div class="flex justify-between mb-4 bg-neural-100/30 rounded-lg p-1 shadow-md">
<button id="tab-prompt" class="flex-1 py-2 font-medium rounded-md bg-gradient-to-br from-primary-400 to-primary-600 text-white text-sm text-center shadow-md">Prompt</button>
<button id="tab-create" class="flex-1 py-2 font-medium rounded-md bg-transparent text-gray-300 text-sm hover:bg-neural-100/50 transition-all text-center">Create</button>
<button id="tab-web" class="flex-1 py-2 font-medium rounded-md bg-transparent text-gray-300 text-sm hover:bg-neural-100/50 transition-all text-center">Web</button>
<button id="tab-data" class="flex-1 py-2 font-medium rounded-md bg-transparent text-gray-300 text-sm hover:bg-neural-100/50 transition-all text-center">Train</button>
<button id="tab-sources" class="flex-1 py-2 font-medium rounded-md bg-transparent text-gray-300 text-sm hover:bg-neural-100/50 transition-all text-center">Sources</button>
</div>
<!-- Prompt Tab Panel -->
<div id="panel-prompt" class="tab-panel">
<div class="glass rounded-xl p-4 shadow-lg border border-gray-700">
<div class="flex gap-2">
<div class="relative flex-grow">
<input id="prompt-input" type="text" placeholder="Ask something..." class="w-full pl-4 pr-10 py-3 text-base rounded-lg border border-gray-700
glass-dark focus:outline-none focus:ring-2 focus:ring-primary-500 text-white placeholder-gray-400">
<button id="mic-btn" class="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-accent-400 transition-colors">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M7 4a3 3 0 016 0v4a3 3 0 11-6 0V4zm4 10.93A7.001 7.001 0 0017 8a1 1 0 10-2 0A5 5 0 015 8a1 1 0 00-2 0 7.001 7.001 0 006 6.93V17H6a1 1 0 100 2h8a1 1 0 100-2h-3v-2.07z" clip-rule="evenodd" />
</svg>
</button>
<!-- Voice recording indicator (hidden by default) -->
<div id="voice-indicator" class="absolute right-3 top-1/2 transform -translate-y-1/2 text-accent-400 hidden">
<div class="voice-wave">
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
<button id="submit-btn" class="px-4 py-2 bg-gradient-to-br from-primary-400 to-primary-600 hover:from-primary-500 hover:to-primary-700 text-white rounded-lg font-medium transition-all shadow-md btn-shine">
Send
</button>
</div>
</div>
</div>
<!-- Create Tab Panel (new) -->
<div id="panel-create" class="tab-panel hidden">
<div class="glass rounded-xl p-4 shadow-lg border border-gray-700">
<!-- Creation type selector -->
<div class="flex justify-between mb-4 bg-neural-100/30 rounded-lg p-1 shadow-md">
<button id="create-type-image" class="flex-1 py-1.5 font-medium rounded-md bg-gradient-to-br from-creative-400 to-creative-600 text-white text-xs text-center shadow-md">
Image
</button>
<button id="create-type-music" class="flex-1 py-1.5 font-medium rounded-md bg-transparent text-gray-300 text-xs hover:bg-neural-100/50 transition-all text-center">
Music
</button>
</div>
<!-- Image generation panel -->
<div id="creation-image" class="creation-panel">
<div class="mb-3">
<label for="image-prompt" class="text-xs text-gray-300 mb-1 block">Image Description</label>
<input type="text" id="image-prompt" placeholder="Abstract flow with geometric shapes..." class="w-full p-2 text-sm rounded-lg border border-gray-700 glass-dark focus:outline-none focus:ring-2 focus:ring-creative-500 text-white placeholder-gray-400">
</div>
<div class="grid grid-cols-2 gap-4 mb-3">
<div>
<label for="image-complexity" class="text-xs text-gray-300 mb-1 block">Complexity</label>
<input type="range" id="image-complexity" min="1" max="10" value="5" class="w-full h-1 accent-creative-500">
</div>
<div>
<label for="image-seed" class="text-xs text-gray-300 mb-1 block">Seed <span class="text-gray-400">(for reproducibility)</span></label>
<input type="number" id="image-seed" min="1" max="999999" value="42" class="w-full p-1.5 text-sm rounded-lg border border-gray-700 glass-dark focus:outline-none focus:ring-2 focus:ring-creative-500 text-white">
</div>
</div>
<div class="mb-3">
<label class="text-xs text-gray-300 mb-1 block">Color Palette</label>
<div class="flex space-x-2">
<div class="color-swatch active" style="background: linear-gradient(to right, #5D5CDE, #32e2df)" data-palette="neural"></div>
<div class="color-swatch" style="background: linear-gradient(to right, #FF416C, #FF4B2B)" data-palette="sunset"></div>
<div class="color-swatch" style="background: linear-gradient(to right, #56CCF2, #2F80ED)" data-palette="ocean"></div>
<div class="color-swatch" style="background: linear-gradient(to right, #EECDA3, #EF629F)" data-palette="peach"></div>
<div class="color-swatch" style="background: linear-gradient(to right, #8E2DE2, #4A00E0)" data-palette="royal"></div>
<div class="color-swatch" style="background: linear-gradient(to right, #F2994A, #F2C94C)" data-palette="amber"></div>
</div>
</div>
<div class="glass-dark rounded-lg overflow-hidden mb-3">
<canvas id="image-canvas" width="300" height="200" class="w-full h-40 object-cover"></canvas>
</div>
<div class="flex space-x-2">
<button id="generate-image" class="flex-1 px-3 py-2 bg-gradient-to-br from-creative-400 to-creative-600 hover:from-creative-500 hover:to-creative-700 text-white rounded-lg text-sm transition-all shadow-md btn-shine">
Generate Image
</button>
<button id="download-image" class="px-3 py-2 bg-neural-100 hover:bg-neural-200 text-white rounded-lg text-sm transition-all shadow-md" disabled>
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
</svg>
</button>
</div>
</div>
<!-- Music generation panel -->
<div id="creation-music" class="creation-panel hidden">
<div class="grid grid-cols-2 gap-4 mb-3">
<div>
<label for="music-scale" class="text-xs text-gray-300 mb-1 block">Musical Scale</label>
<select id="music-scale" class="w-full p-1.5 text-sm rounded-lg border border-gray-700 glass-dark focus:outline-none focus:ring-2 focus:ring-creative-500 text-white">
<option value="major">Major (Happy)</option>
<option value="minor">Minor (Melancholic)</option>
<option value="pentatonic" selected>Pentatonic (Smooth)</option>
<option value="blues">Blues (Soulful)</option>
</select>
</div>
<div>
<label for="music-tempo" class="text-xs text-gray-300 mb-1 block">Tempo (BPM)</label>
<input type="range" id="music-tempo" min="60" max="180" value="120" class="w-full h-1 accent-creative-500">
</div>
</div>
<div class="grid grid-cols-2 gap-4 mb-3">
<div>
<label for="music-complexity" class="text-xs text-gray-300 mb-1 block">Complexity</label>
<input type="range" id="music-complexity" min="1" max="10" value="5" class="w-full h-1 accent-creative-500">
</div>
<div>
<label for="music-instrument" class="text-xs text-gray-300 mb-1 block">Instrument</label>
<select id="music-instrument" class="w-full p-1.5 text-sm rounded-lg border border-gray-700 glass-dark focus:outline-none focus:ring-2 focus:ring-creative-500 text-white">
<option value="sine" selected>Sine (Smooth)</option>
<option value="square">Square (Retro)</option>
<option value="sawtooth">Sawtooth (Sharp)</option>
<option value="triangle">Triangle (Gentle)</option>
</select>
</div>
</div>
<div class="glass-dark rounded-lg p-3 mb-3">
<div id="audio-visualizer" class="h-24 flex items-center justify-center">
<div class="text-gray-500 text-sm">Audio visualizer will appear here</div>
</div>
</div>
<div class="flex space-x-2">
<button id="generate-music" class="flex-1 px-3 py-2 bg-gradient-to-br from-creative-400 to-creative-600 hover:from-creative-500 hover:to-creative-700 text-white rounded-lg text-sm transition-all shadow-md btn-shine">
Generate Music
</button>
<button id="play-music" class="px-3 py-2 bg-neural-100 hover:bg-neural-200 text-white rounded-lg text-sm transition-all shadow-md" disabled>
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</button>
</div>
</div>
</div>
</div>
<!-- Web Tab Panel -->
<div id="panel-web" class="tab-panel hidden">
<div class="glass rounded-xl p-4 shadow-lg border border-gray-700">
<div class="flex flex-col gap-2">
<div class="flex gap-2">
<input id="url-input" type="url" placeholder="Enter website URL" class="w-full px-4 py-3 text-base rounded-lg border border-gray-700
glass-dark focus:outline-none focus:ring-2 focus:ring-primary-500 text-white placeholder-gray-400">
<button id="fetch-btn" class="px-4 py-2 bg-gradient-to-br from-primary-400 to-primary-600 hover:from-primary-500 hover:to-primary-700 text-white rounded-lg font-medium transition-all shadow-md btn-shine">
Fetch
</button>
</div>
<p class="text-xs text-gray-400">
Try URLs from allowed domains like: wikipedia.org, githubusercontent.com
</p>
</div>
<div id="web-preview" class="mt-3 glass-dark rounded-lg p-3 max-h-[150px] overflow-auto hidden"></div>
</div>
</div>
<!-- Training Data Tab Panel -->
<div id="panel-data" class="tab-panel hidden">
<div class="glass rounded-xl p-4 shadow-lg border border-gray-700">
<p class="text-sm mb-2 font-medium text-gray-200">Add new training data:</p>
<textarea id="training-input" rows="3" class="w-full p-3 text-base rounded-lg border border-gray-700
glass-dark focus:outline-none focus:ring-2 focus:ring-primary-500 text-white placeholder-gray-400" placeholder="Enter text to add to model..."></textarea>
<div class="flex justify-between mt-3 gap-2">
<div class="flex-1">
<button id="train-btn" class="w-full px-4 py-2 bg-gradient-to-br from-primary-400 to-primary-600 hover:from-primary-500 hover:to-primary-700 text-white rounded-lg text-sm transition-all shadow-md btn-shine">
Add to Model
</button>
</div>
<div>
<label for="file-upload" class="px-4 py-2 bg-gradient-to-br from-accent-400 to-accent-600 hover:from-accent-500 hover:to-accent-700 text-white rounded-lg text-sm transition-all shadow-md btn-shine cursor-pointer inline-block">
Upload File
<input id="file-upload" type="file" accept=".txt,.text,.md" class="hidden">
</label>
</div>
<div>
<button id="reset-btn" class="px-4 py-2 bg-gradient-to-br from-secondary-400 to-secondary-600 hover:from-secondary-500 hover:to-secondary-700 text-white rounded-lg text-sm transition-all shadow-md btn-shine">
Reset
</button>
</div>
</div>
<div class="mt-4 pt-4 border-t border-gray-700">
<p class="text-sm font-medium mb-2 text-gray-200">Model Statistics:</p>
<div id="model-stats" class="text-sm glass-dark p-3 rounded-lg"></div>
</div>
</div>
</div>
<!-- Data Sources Tab Panel -->
<div id="panel-sources" class="tab-panel hidden">
<div class="glass rounded-xl p-4 shadow-lg border border-gray-700">
<div class="flex justify-between items-center mb-3">
<h3 class="text-sm font-medium text-gray-200">Knowledge Sources</h3>
<span id="source-count" class="bg-gradient-to-r from-primary-500 to-accent-500 text-white px-2 py-0.5 rounded-full text-xs">0</span>
</div>
<div id="data-sources" class="max-h-[200px] overflow-auto text-sm glass-dark rounded-lg p-3">
<p class="text-gray-400">No external data sources added yet</p>
</div>
</div>
</div>
<!-- Floating particles for visual effect -->
<div class="fixed inset-0 pointer-events-none z-0">
<div class="absolute w-3 h-3 rounded-full bg-primary-400/40 top-1/4 left-1/4 animate-float"></div>
<div class="absolute w-2 h-2 rounded-full bg-accent-400/40 top-3/4 left-1/3 animate-float" style="animation-delay: 1s"></div>
<div class="absolute w-4 h-4 rounded-full bg-secondary-400/30 top-1/2 right-1/4 animate-float" style="animation-delay: 0.5s"></div>
<div class="absolute w-2 h-2 rounded-full bg-primary-400/40 bottom-1/4 right-1/3 animate-float" style="animation-delay: 1.5s"></div>
<div class="absolute w-3 h-3 rounded-full bg-creative-400/30 top-1/3 right-1/5 animate-float" style="animation-delay: 0.8s"></div>
</div>
</div>
<script>
// Check for dark mode preference and set it
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.classList.add('dark');
}
// Listen for changes in color scheme preference
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
if (event.matches) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
});
// Tab switching functionality
function switchTab(tabId) {
// Hide all panels
document.querySelectorAll('.tab-panel').forEach(panel => {
panel.classList.add('hidden');
});
// Reset all tab buttons
document.querySelectorAll('[id^="tab-"]').forEach(tab => {
tab.classList.remove('border-primary', 'dark:text-white', 'bg-gradient-to-br', 'from-primary-400', 'to-primary-600');
tab.classList.add('bg-transparent', 'text-gray-300');
});
// Show selected panel and highlight tab
document.getElementById(`panel-${tabId}`).classList.remove('hidden');
const tabButton = document.getElementById(`tab-${tabId}`);
tabButton.classList.remove('bg-transparent', 'text-gray-300');
tabButton.classList.add('bg-gradient-to-br', 'from-primary-400', 'to-primary-600', 'text-white');
}
// Creation tab type switching
function switchCreationType(type) {
// Hide all creation panels
document.querySelectorAll('.creation-panel').forEach(panel => {
panel.classList.add('hidden');
});
// Reset all type buttons
document.querySelectorAll('[id^="create-type-"]').forEach(btn => {
btn.classList.remove('bg-gradient-to-br', 'from-creative-400', 'to-creative-600', 'text-white');
btn.classList.add('bg-transparent', 'text-gray-300');
});
// Show selected panel and highlight button
document.getElementById(`creation-${type}`).classList.remove('hidden');
const typeButton = document.getElementById(`create-type-${type}`);
typeButton.classList.remove('bg-transparent', 'text-gray-300');
typeButton.classList.add('bg-gradient-to-br', 'from-creative-400', 'to-creative-600', 'text-white');
}
// Help navigation
function switchHelpSection(sectionId) {
// Hide all help content sections
document.querySelectorAll('.help-content').forEach(section => {
section.classList.add('hidden');
});
// Remove active class from all steps
document.querySelectorAll('.help-step').forEach(step => {
step.classList.remove('active');
});
// Show selected section and highlight step
document.getElementById(`help-${sectionId}`).classList.remove('hidden');
document.querySelector(`.help-step[data-step="${sectionId}"]`).classList.add('active');
// Update current section
currentHelpSection = sectionId;
}
// Store the current help section
let currentHelpSection = 'neural-network';
// Help section ordering
const helpSections = [
'neural-network',
'language-model',
'voice-features',
'image-generator',
'music-generator',
'training-data',
'tips-tricks'
];
// Simple N-gram based LLM
class SimpleLLM {
constructor() {
// Load from localStorage or initialize with defaults
this.loadOrInitialize();
}
loadOrInitialize() {
try {
const savedData = localStorage.getItem('simpleLLM');
if (savedData) {
const parsed = JSON.parse(savedData);
this.ngramMap = parsed.ngramMap || {};
this.vocabulary = new Set(parsed.vocabulary || []);
this.trainingCount = parsed.trainingCount || 0;
this.dataSources = parsed.dataSources || [];
} else {
this.reset();
// Pre-load with some example data
this.train("Neural networks are systems of interconnected nodes that process data.");
this.train("Large language models learn patterns from text data to generate responses.");
this.train("AI systems use neural networks to understand and generate human language.");
this.train("Machine learning algorithms improve through training on example data.");
this.train("Natural language processing helps computers understand human language.");
this.dataSources = [{
type: "initial",
name: "Initial model data",
date: new Date().toISOString()
}];
}
} catch (error) {
console.error("Error loading model:", error);
this.reset();
}
this.updateStats();
this.updateSourcesList();
}
reset() {
// Initialize model
this.ngramMap = {}; // Maps n-grams to possible next words
this.vocabulary = new Set(); // Set of known words
this.trainingCount = 0;
this.dataSources = [];
// Save empty state
this.save();
this.updateStats();
this.updateSourcesList();
}
save() {
// Convert Set to Array for JSON serialization
const dataToSave = {
ngramMap: this.ngramMap,
vocabulary: Array.from(this.vocabulary),
trainingCount: this.trainingCount,
dataSources: this.dataSources
};
try {
localStorage.setItem('simpleLLM', JSON.stringify(dataToSave));
} catch (error) {
console.error("Error saving model:", error);
}
return dataToSave; // Return for external export
}
// Export model data as JSON file
exportModel() {
const modelData = this.save();
const dataStr = JSON.stringify(modelData, null, 2);
const dataUri = 'data:application/json;charset=utf-8,' + encodeURIComponent(dataStr);
const exportName = 'llm-model-' + new Date().toISOString().slice(0, 10) + '.json';
const linkElement = document.createElement('a');
linkElement.setAttribute('href', dataUri);
linkElement.setAttribute('download', exportName);
linkElement.click();
}
// Import model from JSON file
importModel(jsonData) {
try {
const data = JSON.parse(jsonData);
// Validate data format
if (!data.ngramMap || !Array.isArray(data.vocabulary)) {
throw new Error("Invalid model data format");
}
this.ngramMap = data.ngramMap;
this.vocabulary = new Set(data.vocabulary);
this.trainingCount = data.trainingCount || 0;
this.dataSources = data.dataSources || [];
// Add import source if not already noted
this.dataSources.push({
type: "import",
name: "Imported model data",
date: new Date().toISOString()
});
this.save();
this.updateStats();
this.updateSourcesList();
return true;
} catch (error) {
console.error("Error importing model:", error);
return false;
}
}
tokenize(text) {
// Simple tokenization (split by spaces and punctuation)
return text.toLowerCase()
.replace(/[^\w\s']|_/g, " ") // Replace punctuation with spaces
.replace(/\s+/g, " ") // Collapse multiple spaces
.trim() // Trim leading/trailing spaces
.split(" ") // Split into words
.filter(word => word.length > 0); // Remove empty strings
}
addDataSource(source) {
this.dataSources.push({
...source,
date: new Date().toISOString()
});
this.save();
this.updateSourcesList();
}
train(text, source = null) {
// Tokenize the input text
const tokens = this.tokenize(text);
if (tokens.length < 2) return tokens; // Need at least 2 tokens to train
// Add words to vocabulary
tokens.forEach(token => this.vocabulary.add(token));
// Build n-gram model (for bigrams and trigrams)
for (let i = 0; i < tokens.length - 1; i++) {
// Bigrams
const bigram = tokens[i];
const nextWord = tokens[i + 1];
if (!this.ngramMap[bigram]) {
this.ngramMap[bigram] = [];
}
this.ngramMap[bigram].push(nextWord);
// Trigrams
if (i < tokens.length - 2) {
const trigram = `${tokens[i]} ${tokens[i + 1]}`;
const nextWordAfterTrigram = tokens[i + 2];
if (!this.ngramMap[trigram]) {
this.ngramMap[trigram] = [];
}
this.ngramMap[trigram].push(nextWordAfterTrigram);
}
}
this.trainingCount++;
// Add data source if provided
if (source) {
this.addDataSource(source);
}
this.save();
this.updateStats();
return tokens;
}
generate(prompt, maxLength = 15) {
// Tokenize the prompt
let tokens = this.tokenize(prompt);
// If prompt is empty or no tokens, start with a random word
if (tokens.length === 0) {
const vocabArray = Array.from(this.vocabulary);
if (vocabArray.length === 0) return "Model needs training data first.";
tokens = [vocabArray[Math.floor(Math.random() * vocabArray.length)]];
}
// Keep last 2 tokens for context if we have more
if (tokens.length > 2) {
tokens = tokens.slice(-2);
}
// Generate text
const initialTokens = [...tokens];
const steps = [];
for (let i = 0; i < maxLength; i++) {
// Try to use trigram if available
let nextWordOptions = [];
let contextType = '';
if (tokens.length >= 2) {
const trigram = `${tokens[tokens.length - 2]} ${tokens[tokens.length - 1]}`;
nextWordOptions = this.ngramMap[trigram] || [];
if (nextWordOptions.length > 0) {
contextType = 'trigram';
}
}
// Fall back to bigram if no trigram match
if (nextWordOptions.length === 0 && tokens.length >= 1) {
const bigram = tokens[tokens.length - 1];
nextWordOptions = this.ngramMap[bigram] || [];
if (nextWordOptions.length > 0) {
contextType = 'bigram';
}
}
// If no match, select random word from vocabulary
if (nextWordOptions.length === 0) {
const vocabArray = Array.from(this.vocabulary);
if (vocabArray.length === 0) break;
nextWordOptions = [vocabArray[Math.floor(Math.random() * vocabArray.length)]];
contextType = 'random';
}
// Select a random next word from options
const nextWord = nextWordOptions[Math.floor(Math.random() * nextWordOptions.length)];
tokens.push(nextWord);
// Record this step for visualization
steps.push({
context: tokens.slice(-3, -1).join(' '),
contextType: contextType,
options: nextWordOptions,
selected: nextWord
});
// End if we generated a period
if (nextWord.endsWith('.')) {
break;
}
}
// Format and return
return {
prompt: initialTokens.join(' '),
generated: tokens.slice(initialTokens.length).join(' '),
full: tokens.join(' '),
steps: steps
};
}
getState() {
// Return a sample of the model's internal state for visualization
const ngrams = Object.keys(this.ngramMap).slice(0, 5);
const state = {};
ngrams.forEach(ngram => {
state[ngram] = this.ngramMap[ngram].slice(0, 3);
});
return state;
}
updateStats() {
const statsEl = document.getElementById('model-stats');
if (!statsEl) return;
statsEl.innerHTML = `
<div>Training examples: ${this.trainingCount}</div>
<div>Vocabulary size: ${this.vocabulary.size} words</div>
<div>N-gram patterns: ${Object.keys(this.ngramMap).length}</div>
`;
}
updateSourcesList() {
const sourcesEl = document.getElementById('data-sources');
const countEl = document.getElementById('source-count');
if (!sourcesEl || !countEl) return;
// Update count badge
countEl.textContent = this.dataSources.length;
// No sources yet
if (this.dataSources.length === 0) {
sourcesEl.innerHTML = '<p class="text-gray-500 dark:text-gray-400">No external data sources added yet</p>';
return;
}
// Display the list of sources
sourcesEl.innerHTML = '';
this.dataSources.forEach((source, index) => {
const sourceEl = document.createElement('div');
sourceEl.className = 'py-1 border-b dark:border-gray-700 last:border-b-0';
const date = new Date(source.date);
const dateStr = date.toLocaleDateString() + ' ' + date.toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit'
});
let typeLabel = '';
let typeBgColor = '';
switch (source.type) {
case 'initial':
typeLabel = 'Initial Data';
typeBgColor = 'bg-gray-500';
break;
case 'training':
typeLabel = 'Manual Training';
typeBgColor = 'bg-purple-500';
break;
case 'web':
typeLabel = 'Web Content';
typeBgColor = 'bg-blue-500';
break;
case 'file':
typeLabel = 'Text File';
typeBgColor = 'bg-green-500';
break;
case 'import':
typeLabel = 'Imported';
typeBgColor = 'bg-yellow-500';
break;
default:
typeLabel = 'Unknown';
typeBgColor = 'bg-gray-500';
}
const typeBadge = `<span class="inline-block px-2 py-0.5 text-xs ${typeBgColor} text-white rounded-full mr-2">${typeLabel}</span>`;
sourceEl.innerHTML = `
<div class="flex justify-between">
<div>${typeBadge} ${source.name}</div>
<div class="text-xs text-gray-500 dark:text-gray-400">${dateStr}</div>
</div>
`;
sourcesEl.appendChild(sourceEl);
});
}
}
// Speech Synthesis Controller
class SpeechController {
constructor() {
this.synth = window.speechSynthesis;
this.voices = [];
this.isVoiceEnabled = true;
this.currentUtterance = null;
this.voiceRate = 1.0;
// Initialize voices when they are available
if (this.synth.onvoiceschanged !== undefined) {
this.synth.onvoiceschanged = this.loadVoices.bind(this);
}
// Initial voice loading
this.loadVoices();
// Initialize voice settings UI
this.initializeVoiceSettings();
}
loadVoices() {
// Get available voices
this.voices = this.synth.getVoices();
// Populate voice select
const voiceSelect = document.getElementById('voice-select');
if (voiceSelect) {
voiceSelect.innerHTML = '';
// Sort voices - English first, then by language
const englishVoices = this.voices.filter(voice => voice.lang.includes('en'));
const otherVoices = this.voices.filter(voice => !voice.lang.includes('en'));
const sortedVoices = [...englishVoices, ...otherVoices];
sortedVoices.forEach(voice => {
const option = document.createElement('option');
option.value = voice.name;
option.textContent = `${voice.name} (${voice.lang})`;
voiceSelect.appendChild(option);
});
// Select a default voice (English if available)
if (englishVoices.length > 0) {
voiceSelect.value = englishVoices[0].name;
}
}
}
initializeVoiceSettings() {
const toggleVoiceBtn = document.getElementById('toggle-voice');
const voiceSelect = document.getElementById('voice-select');
const voiceRateInput = document.getElementById('voice-rate');
const rateValue = document.getElementById('rate-value');
// Toggle voice on/off
toggleVoiceBtn.addEventListener('click', () => {
this.isVoiceEnabled = !this.isVoiceEnabled;
document.getElementById('voice-status').textContent =
this.isVoiceEnabled ? 'Voice: ON' : 'Voice: OFF';
// If disabling, stop any current speech
if (!this.isVoiceEnabled) {
this.stop();
}
});
// Update voice selection
voiceSelect.addEventListener('change', () => {
// Stop any current speech when changing voice
this.stop();
});
// Update speech rate
voiceRateInput.addEventListener('input', () => {
this.voiceRate = parseFloat(voiceRateInput.value);
rateValue.textContent = this.voiceRate.toFixed(1);
// If already speaking, update rate in real-time
if (this.currentUtterance) {
this.currentUtterance.rate = this.voiceRate;
}
});
// Direct speak button
document.getElementById('speak-output').addEventListener('click', () => {
// Get output text to speak
const outputViz = document.getElementById('output-viz');
const text = outputViz.textContent.trim();
// Speak only if there's something to say and it's not the placeholder
if (text && !text.includes('Response will appear here')) {
this.speak(text);
}
});
}
speak(text) {
// Don't speak if voice is disabled
if (!this.isVoiceEnabled) return;
// Stop any current speech first
this.stop();
// Create a new utterance
const utterance = new SpeechSynthesisUtterance(text);
// Set voice if selected
const voiceSelect = document.getElementById('voice-select');
if (voiceSelect && voiceSelect.value) {
const selectedVoice = this.voices.find(voice => voice.name === voiceSelect.value);
if (selectedVoice) {
utterance.voice = selectedVoice;
}
}
// Set rate
utterance.rate = this.voiceRate;
// Store the utterance for later control
this.currentUtterance = utterance;
// Speak
this.synth.speak(utterance);
}
stop() {
if (this.synth.speaking) {
this.synth.cancel();
}
this.currentUtterance = null;
}
}
// Speech Recognition Controller
class SpeechRecognitionController {
constructor() {
// Initialize SpeechRecognition with browser prefixes
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
if (SpeechRecognition) {
this.recognition = new SpeechRecognition();
this.recognition.continuous = false;
this.recognition.interimResults = true;
this.recognition.lang = 'en-US';
// Set up event handlers
this.recognition.onstart = this.handleStart.bind(this);
this.recognition.onresult = this.handleResult.bind(this);
this.recognition.onerror = this.handleError.bind(this);
this.recognition.onend = this.handleEnd.bind(this);
this.isListening = false;
this.initializeMicButton();
} else {
console.error('Speech recognition not supported in this browser');
document.getElementById('mic-btn').style.display = 'none';
}
}
initializeMicButton() {
const micBtn = document.getElementById('mic-btn');
const voiceIndicator = document.getElementById('voice-indicator');
micBtn.addEventListener('click', () => {
if (this.isListening) {
this.stop();
} else {
this.start();
}
});
}
start() {
if (!this.isListening) {
try {
this.recognition.start();
} catch (e) {
console.error('Speech recognition error:', e);
}
}
}
stop() {
if (this.isListening) {
this.recognition.stop();
}
}
handleStart() {
this.isListening = true;
// Show voice indicator, hide mic button
document.getElementById('mic-btn').style.display = 'none';
document.getElementById('voice-indicator').style.display = 'block';
// Clear input field
document.getElementById('prompt-input').value = '';
}
handleResult(event) {
const transcript = Array.from(event.results)
.map(result => result[0].transcript)
.join('');
// Update input field with recognized text
const inputField = document.getElementById('prompt-input');
inputField.value = transcript;
// If this is a final result and we have something to submit, do it
if (event.results[0].isFinal && transcript.trim() !== '') {
// Wait a brief moment before submitting to show the final transcript
setTimeout(() => {
if (this.isListening) { // Check if still listening before submitting
document.getElementById('submit-btn').click();
}
}, 500);
}
}
handleError(event) {
console.error('Speech recognition error', event.error);
// Show error feedback
const inputField = document.getElementById('prompt-input');
inputField.placeholder = 'Speech recognition error. Try again.';
// Reset after short delay
setTimeout(() => {
inputField.placeholder = 'Ask something...';
}, 3000);
this.resetUI();
}
handleEnd() {
this.isListening = false;
this.resetUI();
}
resetUI() {
// Hide voice indicator, show mic button
document.getElementById('mic-btn').style.display = 'block';
document.getElementById('voice-indicator').style.display = 'none';
}
}
// AI Image Generator
class ImageGenerator {
constructor() {
this.canvas = document.getElementById('image-canvas');
this.ctx = this.canvas.getContext('2d');
this.complexity = 5; // Default complexity
this.seed = 42; // Default seed
this.palette = 'neural'; // Default color palette
this.hasImage = false;
// Initialize event listeners
this.initializeControls();
}
initializeControls() {
// Generate button
document.getElementById('generate-image').addEventListener('click', () => {
this.generateImage();
});
// Download button
document.getElementById('download-image').addEventListener('click', () => {
this.downloadImage();
});
// Complexity slider
document.getElementById('image-complexity').addEventListener('input', (e) => {
this.complexity = parseInt(e.target.value);
});
// Seed input
document.getElementById('image-seed').addEventListener('change', (e) => {
this.seed = parseInt(e.target.value);
});
// Color palette selection
document.querySelectorAll('.color-swatch').forEach(swatch => {
swatch.addEventListener('click', () => {
// Remove active class from all swatches
document.querySelectorAll('.color-swatch').forEach(s => {
s.classList.remove('active');
});
// Add active class to clicked swatch
swatch.classList.add('active');
// Update palette
this.palette = swatch.dataset.palette;
});
});
}
// Seeded random number generator for reproducible results
seededRandom() {
const x = Math.sin(this.seed++) * 10000;
return x - Math.floor(x);
}
// Get colors for the selected palette
getColors() {
const palettes = {
neural: ['#5D5CDE', '#32e2df', '#7F7FFE', '#1bc5c2'],
sunset: ['#FF416C', '#FF4B2B', '#f7b733', '#fc4a1a'],
ocean: ['#56CCF2', '#2F80ED', '#38ef7d', '#11998e'],
peach: ['#EECDA3', '#EF629F', '#ee9ca7', '#ffdde1'],
royal: ['#8E2DE2', '#4A00E0', '#6a3093', '#a044ff'],
amber: ['#F2994A', '#F2C94C', '#f7971e', '#FFD200']
};
return palettes[this.palette] || palettes.neural;
}
// Generate a procedural image based on settings
generateImage() {
const width = this.canvas.width;
const height = this.canvas.height;
const colors = this.getColors();
const prompt = document.getElementById('image-prompt').value.toLowerCase() || 'abstract pattern';
// Clear canvas
this.ctx.clearRect(0, 0, width, height);
// Draw background gradient
const bgGradient = this.ctx.createLinearGradient(0, 0, width, height);
bgGradient.addColorStop(0, colors[0] + '22'); // Semi-transparent
bgGradient.addColorStop(1, colors[1] + '22');
this.ctx.fillStyle = bgGradient;
this.ctx.fillRect(0, 0, width, height);
// Choose techniques based on prompt
const techniques = {
wave: prompt.includes('wave') || prompt.includes('flow') || this.seededRandom() > 0.7,
circle: prompt.includes('circle') || prompt.includes('round') || this.seededRandom() > 0.7,
line: prompt.includes('line') || prompt.includes('stripe') || this.seededRandom() > 0.7,
dot: prompt.includes('dot') || prompt.includes('point') || this.seededRandom() > 0.7,
spiral: prompt.includes('spiral') || prompt.includes('swirl') || this.seededRandom() > 0.8,
fractal: prompt.includes('fractal') || prompt.includes('complex') || this.seededRandom() > 0.9,
geometric: prompt.includes('geometric') || prompt.includes('pattern') || this.seededRandom() > 0.6,
organic: prompt.includes('organic') || prompt.includes('natural') || this.seededRandom() > 0.7
};
// Apply chosen techniques
if (techniques.wave) this.drawWaves(width, height, colors);
if (techniques.circle) this.drawCircles(width, height, colors);
if (techniques.line) this.drawLines(width, height, colors);
if (techniques.dot) this.drawDots(width, height, colors);
if (techniques.spiral) this.drawSpirals(width, height, colors);
if (techniques.geometric) this.drawGeometric(width, height, colors);
if (techniques.organic) this.drawOrganic(width, height, colors);
if (techniques.fractal) this.drawFractal(width, height, colors);
// Add noise overlay for texture
this.addNoise(width, height, 0.03);
// Enable download button
document.getElementById('download-image').disabled = false;
this.hasImage = true;
}
drawWaves(width, height, colors) {
const numWaves = 3 + Math.floor(this.complexity * 0.8);
for (let i = 0; i < numWaves; i++) {
const color = colors[i % colors.length];
const amplitude = 10 + (this.seededRandom() * this.complexity * 5);
const frequency = 0.01 + (this.seededRandom() * 0.05);
const phase = this.seededRandom() * Math.PI * 2;
const opacity = 0.1 + (this.seededRandom() * 0.3);
this.ctx.beginPath();
for (let x = 0; x < width; x++) {
const y = (height / 2) + Math.sin(x * frequency + phase) * amplitude;
if (x === 0) {
this.ctx.moveTo(x, y);
} else {
this.ctx.lineTo(x, y);
}
}
this.ctx.strokeStyle = color + Math.floor(opacity * 255).toString(16).padStart(2, '0');
this.ctx.lineWidth = 2 + (this.seededRandom() * 4);
this.ctx.stroke();
}
}
drawCircles(width, height, colors) {
const numCircles = 5 + Math.floor(this.complexity * 0.7);
for (let i = 0; i < numCircles; i++) {
const color = colors[i % colors.length];
const x = this.seededRandom() * width;
const y = this.seededRandom() * height;
const radius = 5 + (this.seededRandom() * this.complexity * 8);
const opacity = 0.1 + (this.seededRandom() * 0.4);
this.ctx.beginPath();
this.ctx.arc(x, y, radius, 0, Math.PI * 2);
this.ctx.fillStyle = color + Math.floor(opacity * 255).toString(16).padStart(2, '0');
this.ctx.fill();
}
}
drawLines(width, height, colors) {
const numLines = 5 + Math.floor(this.complexity * 1.2);
for (let i = 0; i < numLines; i++) {
const color = colors[i % colors.length];
const startX = this.seededRandom() * width;
const startY = this.seededRandom() * height;
const endX = this.seededRandom() * width;
const endY = this.seededRandom() * height;
const opacity = 0.1 + (this.seededRandom() * 0.5);
this.ctx.beginPath();
this.ctx.moveTo(startX, startY);
this.ctx.lineTo(endX, endY);
this.ctx.strokeStyle = color + Math.floor(opacity * 255).toString(16).padStart(2, '0');
this.ctx.lineWidth = 1 + (this.seededRandom() * 3);
this.ctx.stroke();
}
}
drawDots(width, height, colors) {
const numDots = 100 + (this.complexity * 50);
for (let i = 0; i < numDots; i++) {
const color = colors[i % colors.length];
const x = this.seededRandom() * width;
const y = this.seededRandom() * height;
const radius = 0.5 + (this.seededRandom() * 2);
const opacity = 0.2 + (this.seededRandom() * 0.5);
this.ctx.beginPath();
this.ctx.arc(x, y, radius, 0, Math.PI * 2);
this.ctx.fillStyle = color + Math.floor(opacity * 255).toString(16).padStart(2, '0');
this.ctx.fill();
}
}
drawSpirals(width, height, colors) {
const centerX = width / 2;
const centerY = height / 2;
const maxRadius = Math.min(width, height) / 2 * 0.8;
const numSpirals = 1 + Math.floor(this.complexity * 0.4);
for (let s = 0; s < numSpirals; s++) {
const color = colors[s % colors.length];
const tightness = 0.2 + (this.seededRandom() * 0.6);
const opacity = 0.2 + (this.seededRandom() * 0.6);
const startAngle = this.seededRandom() * Math.PI * 2;
const direction = this.seededRandom() > 0.5 ? 1 : -1;
this.ctx.beginPath();
for (let i = 0; i < 100 * this.complexity; i++) {
const angle = startAngle + (direction * i * tightness * 0.1);
const radius = (i / (100 * this.complexity)) * maxRadius;
const x = centerX + Math.cos(angle) * radius;
const y = centerY + Math.sin(angle) * radius;
if (i === 0) {
this.ctx.moveTo(x, y);
} else {
this.ctx.lineTo(x, y);
}
}
this.ctx.strokeStyle = color + Math.floor(opacity * 255).toString(16).padStart(2, '0');
this.ctx.lineWidth = 1 + (this.seededRandom() * 2);
this.ctx.stroke();
}
}
drawGeometric(width, height, colors) {
const numShapes = 3 + Math.floor(this.complexity * 0.8);
for (let i = 0; i < numShapes; i++) {
const color = colors[i % colors.length];
const x = this.seededRandom() * width;
const y = this.seededRandom() * height;
const size = 10 + (this.seededRandom() * this.complexity * 5);
const opacity = 0.1 + (this.seededRandom() * 0.3);
const rotation = this.seededRandom() * Math.PI * 2;
this.ctx.save();
this.ctx.translate(x, y);
this.ctx.rotate(rotation);
// Choose shape type
const shapeType = Math.floor(this.seededRandom() * 3);
this.ctx.beginPath();
if (shapeType === 0) {
// Square
this.ctx.rect(-size / 2, -size / 2, size, size);
} else if (shapeType === 1) {
// Triangle
this.ctx.moveTo(0, -size / 2);
this.ctx.lineTo(-size / 2, size / 2);
this.ctx.lineTo(size / 2, size / 2);
this.ctx.closePath();
} else {
// Hexagon
for (let j = 0; j < 6; j++) {
const angle = (j * Math.PI) / 3;
const hx = Math.cos(angle) * size / 2;
const hy = Math.sin(angle) * size / 2;
if (j === 0) {
this.ctx.moveTo(hx, hy);
} else {
this.ctx.lineTo(hx, hy);
}
}
this.ctx.closePath();
}
this.ctx.fillStyle = color + Math.floor(opacity * 255).toString(16).padStart(2, '0');
this.ctx.fill();
this.ctx.restore();
}
}
drawOrganic(width, height, colors) {
const numShapes = 3 + Math.floor(this.complexity * 0.7);
for (let i = 0; i < numShapes; i++) {
const color = colors[i % colors.length];
const centerX = this.seededRandom() * width;
const centerY = this.seededRandom() * height;
const radius = 10 + (this.seededRandom() * this.complexity * 7);
const opacity = 0.1 + (this.seededRandom() * 0.3);
this.ctx.beginPath();
// Create organic shape with perlin-like noise
const points = 10 + Math.floor(this.seededRandom() * 8);
for (let j = 0; j <= points; j++) {
const angle = (j / points) * Math.PI * 2;
const noise = 0.5 + (this.seededRandom() * 0.5);
const adjustedRadius = radius * noise;
const x = centerX + Math.cos(angle) * adjustedRadius;
const y = centerY + Math.sin(angle) * adjustedRadius;
if (j === 0) {
this.ctx.moveTo(x, y);
} else {
const cpx1 = centerX + Math.cos(angle - 0.1) * adjustedRadius * 1.2;
const cpy1 = centerY + Math.sin(angle - 0.1) * adjustedRadius * 1.2;
this.ctx.quadraticCurveTo(cpx1, cpy1, x, y);
}
}
this.ctx.fillStyle = color + Math.floor(opacity * 255).toString(16).padStart(2, '0');
this.ctx.fill();
}
}
drawFractal(width, height, colors) {
// Simple recursive pattern
const maxDepth = Math.floor(2 + (this.complexity * 0.5));
const color = colors[Math.floor(this.seededRandom() * colors.length)];
const drawBranch = (x, y, length, angle, depth) => {
if (depth > maxDepth) return;
const endX = x + Math.cos(angle) * length;
const endY = y + Math.sin(angle) * length;
this.ctx.beginPath();
this.ctx.moveTo(x, y);
this.ctx.lineTo(endX, endY);
const opacity = 0.7 - (depth / maxDepth * 0.5);
this.ctx.strokeStyle = color + Math.floor(opacity * 255).toString(16).padStart(2, '0');
this.ctx.lineWidth = (maxDepth - depth) + 1;
this.ctx.stroke();
const branchAngle = 0.3 + (this.seededRandom() * 0.4);
// Recursive branches
drawBranch(endX, endY, length * 0.67, angle + branchAngle, depth + 1);
drawBranch(endX, endY, length * 0.67, angle - branchAngle, depth + 1);
};
// Start the fractal
drawBranch(width * 0.5, height * 0.7, height * 0.15, -Math.PI / 2, 0);
}
addNoise(width, height, intensity) {
const imageData = this.ctx.getImageData(0, 0, width, height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const noise = this.seededRandom() * intensity;
data[i] = Math.min(255, Math.max(0, data[i] * (1 + noise)));
data[i + 1] = Math.min(255, Math.max(0, data[i + 1] * (1 + noise)));
data[i + 2] = Math.min(255, Math.max(0, data[i + 2] * (1 + noise)));
}
this.ctx.putImageData(imageData, 0, 0);
}
// Download the generated image
downloadImage() {
if (!this.hasImage) return;
// Create a temporary link
const link = document.createElement('a');
link.download = 'neural-art-' + Math.floor(Math.random() * 10000) + '.png';
link.href = this.canvas.toDataURL('image/png');
link.click();
}
}
// AI Music Generator
class MusicGenerator {
constructor() {
// Check if Web Audio API is supported
this.isAudioSupported = typeof window.AudioContext !== 'undefined' ||
typeof window.webkitAudioContext !== 'undefined';
if (this.isAudioSupported) {
this.audioContext = new(window.AudioContext || window.webkitAudioContext)();
this.visualizer = document.getElementById('audio-visualizer');
this.isPlaying = false;
this.sequence = [];
this.currentNote = 0;
this.analyser = null;
this.dataArray = null;
this.rafId = null;
// Initialize UI
this.initializeControls();
} else {
// Show unsupported message
document.getElementById('audio-visualizer').innerHTML =
'<div class="text-secondary-400 text-sm">Web Audio API is not supported in this browser</div>';
document.getElementById('generate-music').disabled = true;
document.getElementById('play-music').disabled = true;
}
}
initializeControls() {
// Generate button
document.getElementById('generate-music').addEventListener('click', () => {
this.generateMusic();
});
// Play button
document.getElementById('play-music').addEventListener('click', () => {
if (this.isPlaying) {
this.stopMusic();
} else {
this.playMusic();
}
});
// Initialize visualizer
this.setupVisualizer();
}
// Setup audio analyzer for visualization
setupVisualizer() {
this.analyser = this.audioContext.createAnalyser();
this.analyser.fftSize = 256;
const bufferLength = this.analyser.frequencyBinCount;
this.dataArray = new Uint8Array(bufferLength);
// Create visualization bars
this.visualizer.innerHTML = '';
for (let i = 0; i < 32; i++) {
const bar = document.createElement('div');
bar.className = 'audio-bar h-0';
this.visualizer.appendChild(bar);
}
}
// Generate a melody based on current settings
generateMusic() {
// Get settings
const scale = document.getElementById('music-scale').value;
const complexity = parseInt(document.getElementById('music-complexity').value);
// Define scale patterns
const scales = {
major: [0, 2, 4, 5, 7, 9, 11, 12],
minor: [0, 2, 3, 5, 7, 8, 10, 12],
pentatonic: [0, 2, 4, 7, 9, 12],
blues: [0, 3, 5, 6, 7, 10, 12]
};
const selectedScale = scales[scale] || scales.pentatonic;
const baseNote = 60; // Middle C
const sequenceLength = 16 + (complexity * 4); // More complexity = longer sequence
// Create melody sequence
this.sequence = [];
let prevNote = null;
for (let i = 0; i < sequenceLength; i++) {
// Choose a note from the scale
const octaveShift = Math.floor(this.seededRandom() * 3) - 1; // -1, 0, or 1 octave shift
const scaleIndex = Math.floor(this.seededRandom() * selectedScale.length);
// More complex patterns have higher probability of rests and jumps
const isRest = this.seededRandom() < (0.1 + (complexity * 0.02));
if (isRest) {
// Rest note (no sound)
this.sequence.push(null);
} else {
// Calculate note frequency
const note = baseNote + selectedScale[scaleIndex] + (octaveShift * 12);
const frequency = 440 * Math.pow(2, (note - 69) / 12); // Convert MIDI note to frequency
// Duration depends on complexity
const duration = 0.1 + (this.seededRandom() * 0.2);
// Smoother progression for less complex melodies
if (prevNote && complexity < 5) {
// Avoid large jumps for smoother melodies
const maxJump = 7 + (complexity * 2);
if (Math.abs(note - prevNote) > maxJump) {
// Choose a closer note
const direction = note > prevNote ? 1 : -1;
const newNote = prevNote + (direction * (this.seededRandom() * maxJump));
this.sequence.push({
frequency: 440 * Math.pow(2, (newNote - 69) / 12),
duration
});
prevNote = newNote;
continue;
}
}
this.sequence.push({
frequency,
duration
});
prevNote = note;
}
}
// Enable play button
document.getElementById('play-music').disabled = false;
this.visualizeSequence();
}
// Play the generated melody
playMusic() {
if (!this.sequence.length) return;
if (this.isPlaying) this.stopMusic();
this.isPlaying = true;
this.currentNote = 0;
// Update play button to stop icon
const playBtn = document.getElementById('play-music');
playBtn.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 9v6m4-6v6m7-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
`;
// Get tempo and instrument
const tempo = parseInt(document.getElementById('music-tempo').value);
const instrument = document.getElementById('music-instrument').value;
// Calculate time between notes
const noteInterval = 60000 / tempo; // Convert BPM to milliseconds
// Start playback
this.startVisualization();
this.playNextNote(instrument, noteInterval);
}
// Play each note in sequence
playNextNote(instrument, interval) {
if (!this.isPlaying || this.currentNote >= this.sequence.length) {
this.stopMusic();
return;
}
const note = this.sequence[this.currentNote];
if (note) {
// Create oscillator
const oscillator = this.audioContext.createOscillator();
const gainNode = this.audioContext.createGain();
// Set waveform type
oscillator.type = instrument;
oscillator.frequency.value = note.frequency;
// Set envelope
gainNode.gain.value = 0;
gainNode.gain.setValueAtTime(0, this.audioContext.currentTime);
gainNode.gain.linearRampToValueAtTime(0.5, this.audioContext.currentTime + 0.01);
gainNode.gain.exponentialRampToValueAtTime(0.001, this.audioContext.currentTime + note.duration);
// Connect nodes
oscillator.connect(gainNode);
gainNode.connect(this.analyser);
this.analyser.connect(this.audioContext.destination);
// Start and stop oscillator
oscillator.start();
oscillator.stop(this.audioContext.currentTime + note.duration);
}
// Move to next note
this.currentNote++;
// Schedule next note
setTimeout(() => {
this.playNextNote(instrument, interval);
}, interval);
}
// Stop playback
stopMusic() {
this.isPlaying = false;
this.currentNote = 0;
// Update play button back to play icon
const playBtn = document.getElementById('play-music');
playBtn.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
`;
// Stop visualization
this.stopVisualization();
}
// Start audio visualization
startVisualization() {
// Cancel any existing animation
if (this.rafId) {
cancelAnimationFrame(this.rafId);
}
const bars = document.querySelectorAll('.audio-bar');
const visualize = () => {
this.analyser.getByteFrequencyData(this.dataArray);
for (let i = 0; i < bars.length; i++) {
// Map analyzer data to bar heights
const index = Math.floor(i * this.dataArray.length / bars.length);
const value = this.dataArray[index] / 255;
// Set bar height
const height = value * 80; // Max height in pixels
bars[i].style.height = height + 'px';
}
this.rafId = requestAnimationFrame(visualize);
};
visualize();
}
// Stop visualization
stopVisualization() {
if (this.rafId) {
cancelAnimationFrame(this.rafId);
this.rafId = null;
}
// Reset bars
const bars = document.querySelectorAll('.audio-bar');
for (let i = 0; i < bars.length; i++) {
bars[i].style.height = '0px';
}
}
// Visualize the sequence pattern without playing
visualizeSequence() {
if (!this.sequence.length) return;
// Reset visualizer
this.visualizer.innerHTML = '';
// Create sequencer grid
const grid = document.createElement('div');
grid.className = 'flex items-center justify-center h-full w-full';
// Create visual representation of sequence
const sequenceContainer = document.createElement('div');
sequenceContainer.className = 'flex flex-wrap justify-center gap-1 p-2';
// Show a subset of the sequence for clarity
const displayCount = Math.min(this.sequence.length, 32);
for (let i = 0; i < displayCount; i++) {
const note = this.sequence[i];
const noteEl = document.createElement('div');
if (note === null) {
// Rest
noteEl.className = 'w-4 h-4 rounded-full bg-gray-700';
} else {
// Map frequency to color
const hue = ((note.frequency - 100) / 1000 * 360) % 360;
// Map frequency to height
const height = Math.min(40, Math.max(4, ((note.frequency - 100) / 1000) * 40));
noteEl.className = 'w-4 rounded-sm';
noteEl.style.height = `${height}px`;
noteEl.style.backgroundColor = `hsl(${hue}, 80%, 60%)`;
}
sequenceContainer.appendChild(noteEl);
}
grid.appendChild(sequenceContainer);
this.visualizer.appendChild(grid);
}
// Seeded random for reproducible results
seededRandom() {
const x = Math.sin(this.seed++) * 10000;
return x - Math.floor(x);
}
// Set random seed
setSeed(seed) {
this.seed = seed;
}
}
// Initialize model
const llm = new SimpleLLM();
// Initialize speech controllers
let speechSynth, speechRecognition, imageGenerator, musicGenerator;
// Neural network visualization
const svgElement = document.getElementById('neural-network');
// Initialize neural network visualization
function initializeNeuralNetwork() {
const width = svgElement.clientWidth;
const height = svgElement.clientHeight;
const nodeRadius = 4;
const layerSpacing = width / 6;
// Define layers (input, hidden layers, output)
const layers = [{
name: "Input",
nodes: 6,
x: layerSpacing,
color: "#5D5CDE"
},
{
name: "Hidden",
nodes: 8,
x: layerSpacing * 3,
color: "#32e2df"
},
{
name: "Output",
nodes: 6,
x: layerSpacing * 5,
color: "#ff6b6b"
}
];
// Create gradient definitions
const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
svgElement.innerHTML = '';
svgElement.appendChild(defs);
// Create glowing filter
const filter = document.createElementNS("http://www.w3.org/2000/svg", "filter");
filter.setAttribute("id", "glow");
filter.setAttribute("x", "-50%");
filter.setAttribute("y", "-50%");
filter.setAttribute("width", "200%");
filter.setAttribute("height", "200%");
const feGaussianBlur = document.createElementNS("http://www.w3.org/2000/svg", "feGaussianBlur");
feGaussianBlur.setAttribute("stdDeviation", "2");
feGaussianBlur.setAttribute("result", "blur");
filter.appendChild(feGaussianBlur);
const feComposite = document.createElementNS("http://www.w3.org/2000/svg", "feComposite");
feComposite.setAttribute("in", "SourceGraphic");
feComposite.setAttribute("in2", "blur");
feComposite.setAttribute("operator", "over");
filter.appendChild(feComposite);
defs.appendChild(filter);
// Create connection gradient
const connGradient = document.createElementNS("http://www.w3.org/2000/svg", "linearGradient");
connGradient.setAttribute("id", "connGradient");
connGradient.setAttribute("gradientUnits", "userSpaceOnUse");
const stop1 = document.createElementNS("http://www.w3.org/2000/svg", "stop");
stop1.setAttribute("offset", "0%");
stop1.setAttribute("stop-color", "rgba(93, 92, 222, 0.7)");
connGradient.appendChild(stop1);
const stop2 = document.createElementNS("http://www.w3.org/2000/svg", "stop");
stop2.setAttribute("offset", "100%");
stop2.setAttribute("stop-color", "rgba(50, 226, 223, 0.7)");
connGradient.appendChild(stop2);
defs.appendChild(connGradient);
// Create radial gradients for nodes
layers.forEach((layer, i) => {
const gradient = document.createElementNS("http://www.w3.org/2000/svg", "radialGradient");
gradient.setAttribute("id", `nodeGradient${i}`);
const stop1 = document.createElementNS("http://www.w3.org/2000/svg", "stop");
stop1.setAttribute("offset", "0%");
stop1.setAttribute("stop-color", layer.color);
const stop2 = document.createElementNS("http://www.w3.org/2000/svg", "stop");
stop2.setAttribute("offset", "80%");
stop2.setAttribute("stop-color", layer.color);
const stop3 = document.createElementNS("http://www.w3.org/2000/svg", "stop");
stop3.setAttribute("offset", "100%");
stop3.setAttribute("stop-color", `${layer.color}80`);
gradient.appendChild(stop1);
gradient.appendChild(stop2);
gradient.appendChild(stop3);
defs.appendChild(gradient);
});
// Add a subtle background grid
const gridSize = 15;
const gridGroup = document.createElementNS("http://www.w3.org/2000/svg", "g");
gridGroup.classList.add("grid");
// Horizontal grid lines
for (let y = 0; y < height; y += gridSize) {
const line = document.createElementNS("http://www.w3.org/2000/svg", "line");
line.setAttribute("x1", "0");
line.setAttribute("y1", y);
line.setAttribute("x2", width);
line.setAttribute("y2", y);
line.setAttribute("stroke", "rgba(93, 92, 222, 0.05)");
line.setAttribute("stroke-width", "1");
gridGroup.appendChild(line);
}
// Vertical grid lines
for (let x = 0; x < width; x += gridSize) {
const line = document.createElementNS("http://www.w3.org/2000/svg", "line");
line.setAttribute("x1", x);
line.setAttribute("y1", "0");
line.setAttribute("x2", x);
line.setAttribute("y2", height);
line.setAttribute("stroke", "rgba(93, 92, 222, 0.05)");
line.setAttribute("stroke-width", "1");
gridGroup.appendChild(line);
}
svgElement.appendChild(gridGroup);
// Draw connections first (behind nodes)
const connGroup = document.createElementNS("http://www.w3.org/2000/svg", "g");
connGroup.classList.add("connections");
for (let i = 0; i < layers.length - 1; i++) {
const currentLayer = layers[i];
const nextLayer = layers[i + 1];
const currentNodeSpacing = height / (currentLayer.nodes + 1);
const nextNodeSpacing = height / (nextLayer.nodes + 1);
for (let j = 0; j < currentLayer.nodes; j++) {
const startX = currentLayer.x;
const startY = currentNodeSpacing * (j + 1);
for (let k = 0; k < nextLayer.nodes; k++) {
const endX = nextLayer.x;
const endY = nextNodeSpacing * (k + 1);
// Update conn gradient with correct positions
const connId = `conn-${i}-${j}-${k}`;
const connGradient = document.createElementNS("http://www.w3.org/2000/svg", "linearGradient");
connGradient.setAttribute("id", connId);
connGradient.setAttribute("gradientUnits", "userSpaceOnUse");
connGradient.setAttribute("x1", startX);
connGradient.setAttribute("y1", startY);
connGradient.setAttribute("x2", endX);
connGradient.setAttribute("y2", endY);
const stop1 = document.createElementNS("http://www.w3.org/2000/svg", "stop");
stop1.setAttribute("offset", "0%");
stop1.setAttribute("stop-color", currentLayer.color + "50");
connGradient.appendChild(stop1);
const stop2 = document.createElementNS("http://www.w3.org/2000/svg", "stop");
stop2.setAttribute("offset", "100%");
stop2.setAttribute("stop-color", nextLayer.color + "50");
connGradient.appendChild(stop2);
defs.appendChild(connGradient);
// Draw connection with custom gradient
const line = document.createElementNS("http://www.w3.org/2000/svg", "line");
line.setAttribute("x1", startX);
line.setAttribute("y1", startY);
line.setAttribute("x2", endX);
line.setAttribute("y2", endY);
line.setAttribute("stroke", `url(#${connId})`);
line.setAttribute("stroke-width", "1");
line.setAttribute("data-path", `M${startX},${startY} L${endX},${endY}`);
line.classList.add("connection");
// Add connection to the group
connGroup.appendChild(line);
}
}
}
svgElement.appendChild(connGroup);
// Create a group for the data packets
const packetGroup = document.createElementNS("http://www.w3.org/2000/svg", "g");
packetGroup.classList.add("packets");
svgElement.appendChild(packetGroup);
// Create node groups for layers
for (let i = 0; i < layers.length; i++) {
const layer = layers[i];
const nodeSpacing = height / (layer.nodes + 1);
// Create a group for each layer
const layerGroup = document.createElementNS("http://www.w3.org/2000/svg", "g");
layerGroup.classList.add(`layer-${layer.name.toLowerCase()}-group`);
// Add subtle glow behind the layer
const layerGlow = document.createElementNS("http://www.w3.org/2000/svg", "ellipse");
layerGlow.setAttribute("cx", layer.x);
layerGlow.setAttribute("cy", height / 2);
layerGlow.setAttribute("rx", 15);
layerGlow.setAttribute("ry", height / 2.5);
layerGlow.setAttribute("fill", `${layer.color}15`);
layerGlow.setAttribute("filter", "blur(15px)");
svgElement.appendChild(layerGlow);
for (let j = 0; j < layer.nodes; j++) {
const nodeY = nodeSpacing * (j + 1);
// Create glow effect (optional)
const glow = document.createElementNS("http://www.w3.org/2000/svg", "circle");
glow.setAttribute("cx", layer.x);
glow.setAttribute("cy", nodeY);
glow.setAttribute("r", nodeRadius * 2.5);
glow.setAttribute("fill", `${layer.color}20`);
glow.setAttribute("filter", "blur(3px)");
layerGroup.appendChild(glow);
// Create node
const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
circle.setAttribute("cx", layer.x);
circle.setAttribute("cy", nodeY);
circle.setAttribute("r", nodeRadius);
circle.setAttribute("fill", `url(#nodeGradient${i})`);
circle.setAttribute("stroke", layer.color);
circle.setAttribute("stroke-width", "0.5");
circle.classList.add("node");
circle.classList.add(`layer-${layer.name.toLowerCase()}`);
// Add a small rotation effect to some nodes
if (j % 2 === 0) {
const animation = document.createElementNS("http://www.w3.org/2000/svg", "animateTransform");
animation.setAttribute("attributeName", "transform");
animation.setAttribute("type", "rotate");
animation.setAttribute("from", `0 ${layer.x} ${nodeY}`);
animation.setAttribute("to", `360 ${layer.x} ${nodeY}`);
animation.setAttribute("dur", `${20 + j * 5}s`);
animation.setAttribute("repeatCount", "indefinite");
circle.appendChild(animation);
}
layerGroup.appendChild(circle);
}
// Add layer label
const text = document.createElementNS("http://www.w3.org/2000/svg", "text");
text.setAttribute("x", layer.x);
text.setAttribute("y", height - 5);
text.setAttribute("text-anchor", "middle");
text.setAttribute("font-size", "10px");
text.setAttribute("fill", layer.color);
text.setAttribute("filter", "drop-shadow(0 1px 1px rgba(0,0,0,0.3))");
text.textContent = layer.name;
layerGroup.appendChild(text);
svgElement.appendChild(layerGroup);
}
// Add idle animation to a few random nodes
const nodes = svgElement.querySelectorAll('.node');
const randomCount = Math.min(5, nodes.length);
const randomIndices = [];
while (randomIndices.length < randomCount) {
const idx = Math.floor(Math.random() * nodes.length);
if (!randomIndices.includes(idx)) {
randomIndices.push(idx);
// Apply a subtle pulse animation to random nodes for ambient effect
setTimeout(() => {
nodes[idx].classList.add("pulse-animation");
setTimeout(() => {
nodes[idx].classList.remove("pulse-animation");
}, 3000 + Math.random() * 1000);
}, Math.random() * 5000);
}
}
}
// Create data packet elements for the animation
function createDataPacket(path, color) {
const packetGroup = svgElement.querySelector('.packets');
const packetSize = 4;
// Create packet element
const packet = document.createElementNS("http://www.w3.org/2000/svg", "circle");
packet.setAttribute("r", packetSize);
packet.setAttribute("fill", color);
packet.setAttribute("filter", "drop-shadow(0 0 3px " + color + ")");
packet.style.setProperty('--path', path);
packet.classList.add('data-packet');
packetGroup.appendChild(packet);
// Remove the packet after animation completes
setTimeout(() => {
packet.remove();
}, 1500);
return packet;
}
// Animate data flowing through the network
function animateNetwork(steps = []) {
const connections = svgElement.querySelectorAll('.connection');
const nodes = svgElement.querySelectorAll('.node');
const packetGroup = svgElement.querySelector('.packets');
// Colors for active elements
const inputColor = "#7F7FFE";
const hiddenColor = "#32e2df";
const outputColor = "#ff6b6b";
const selectedColor = "#4CAF50";
// Clear existing packets
packetGroup.innerHTML = '';
// Reset all connections and nodes
connections.forEach(conn => {
conn.setAttribute("stroke-width", "1");
conn.classList.remove("flow-animation");
// Restore original gradient (using the id attribute that contains the gradient info)
if (conn.id && conn.id.startsWith("conn-")) {
conn.setAttribute("stroke", `url(#${conn.id})`);
} else {
conn.setAttribute("stroke", "rgba(93, 92, 222, 0.2)");
}
});
nodes.forEach(node => {
if (node.classList.contains('layer-input')) {
node.setAttribute("fill", "url(#nodeGradient0)");
} else if (node.classList.contains('layer-hidden')) {
node.setAttribute("fill", "url(#nodeGradient1)");
} else if (node.classList.contains('layer-output')) {
node.setAttribute("fill", "url(#nodeGradient2)");
}
node.setAttribute("r", "4");
node.classList.remove("pulse-animation", "pulse-animation-accent", "pulse-animation-secondary");
});
// Animation timing
let delay = 0;
const stepDuration = 800;
// If we have steps from the model, visualize those specifically
if (steps.length > 0) {
// Update model state display
const modelStateEl = document.getElementById('model-state');
modelStateEl.innerHTML = '';
// Show the generation steps with jQuery for smoother animations
steps.forEach((step, index) => {
setTimeout(() => {
// Helper function to activate a node with specific color/animation
const activateNode = (node, color, size, animClass) => {
$(node).animate({
r: size
}, 300);
node.setAttribute("fill", color);
node.setAttribute("filter", "drop-shadow(0 0 5px " + color + "80)");
if (animClass) node.classList.add(animClass);
// Create a pulsing ring around active node
const glow = document.createElementNS("http://www.w3.org/2000/svg", "circle");
glow.setAttribute("cx", node.getAttribute("cx"));
glow.setAttribute("cy", node.getAttribute("cy"));
glow.setAttribute("r", size * 1.5);
glow.setAttribute("fill", "none");
glow.setAttribute("stroke", color);
glow.setAttribute("stroke-width", "1");
glow.setAttribute("opacity", "0.5");
glow.style.animation = "pulse 1.5s infinite ease-out";
node.parentNode.appendChild(glow);
setTimeout(() => {
// Remove glow and animations
glow.remove();
$(node).animate({
r: 4
}, 300);
node.removeAttribute("filter");
if (node.classList.contains('layer-input')) {
node.setAttribute("fill", "url(#nodeGradient0)");
} else if (node.classList.contains('layer-hidden')) {
node.setAttribute("fill", "url(#nodeGradient1)");
} else if (node.classList.contains('layer-output')) {
node.setAttribute("fill", "url(#nodeGradient2)");
}
if (animClass) node.classList.remove(animClass);
}, stepDuration - 200);
};
// Activate input nodes (representing current context)
const inputNodes = document.querySelectorAll('.layer-input');
const contextNodes = Array.from(inputNodes).slice(0, 2);
contextNodes.forEach((node, i) => {
activateNode(node, inputColor, 7, "pulse-animation");
});
// Get random connections from input to hidden, and add data packets
setTimeout(() => {
contextNodes.forEach(inputNode => {
const hiddenNodes = document.querySelectorAll('.layer-hidden');
// Choose 2-3 random hidden nodes to connect to from each input node
const targetCount = 2 + Math.floor(Math.random() * 2);
const targetIndices = [];
while (targetIndices.length < targetCount) {
const idx = Math.floor(Math.random() * hiddenNodes.length);
if (!targetIndices.includes(idx)) {
targetIndices.push(idx);
}
}
// Create data flows to these targets
targetIndices.forEach(idx => {
const target = hiddenNodes[idx];
const startX = inputNode.getAttribute("cx");
const startY = inputNode.getAttribute("cy");
const endX = target.getAttribute("cx");
const endY = target.getAttribute("cy");
// Define path for animation
const path = `M${startX},${startY} L${endX},${endY}`;
// Create active connection with gradient
const conn = document.createElementNS("http://www.w3.org/2000/svg", "line");
conn.setAttribute("x1", startX);
conn.setAttribute("y1", startY);
conn.setAttribute("x2", endX);
conn.setAttribute("y2", endY);
conn.setAttribute("stroke", inputColor);
conn.setAttribute("stroke-width", "2");
conn.setAttribute("opacity", "0.6");
conn.setAttribute("stroke-dasharray", "5,3");
conn.classList.add("flow-animation");
// Add to SVG temporarily
svgElement.appendChild(conn);
// Create data packet flowing along this path
createDataPacket(path, inputColor);
// Remove connection after delay
setTimeout(() => {
conn.remove();
}, 600);
// Activate target node after data arrives
setTimeout(() => {
activateNode(target, hiddenColor, 7, "pulse-animation-accent");
}, 300);
});
});
}, 200);
// Connect hidden to output nodes and flow more data
setTimeout(() => {
const hiddenNodes = document.querySelectorAll('.layer-hidden');
const activatedHiddenIndices = Array.from({
length: hiddenNodes.length
}, (_, i) => i)
.sort(() => Math.random() - 0.5)
.slice(0, 3);
const activeHiddenNodes = activatedHiddenIndices.map(i => hiddenNodes[i]);
// Get output nodes
const outputNodes = document.querySelectorAll('.layer-output');
const numOptions = Math.min(step.options.length, outputNodes.length);
const outputTargets = Array.from(outputNodes).slice(0, numOptions);
// Create connections from hidden to outputs
activeHiddenNodes.forEach(hiddenNode => {
outputTargets.forEach((outputNode, outputIdx) => {
const startX = hiddenNode.getAttribute("cx");
const startY = hiddenNode.getAttribute("cy");
const endX = outputNode.getAttribute("cx");
const endY = outputNode.getAttribute("cy");
// Define path for animation
const path = `M${startX},${startY} L${endX},${endY}`;
// Create active connection with gradient
const conn = document.createElementNS("http://www.w3.org/2000/svg", "line");
conn.setAttribute("x1", startX);
conn.setAttribute("y1", startY);
conn.setAttribute("x2", endX);
conn.setAttribute("y2", endY);
conn.setAttribute("stroke", hiddenColor);
conn.setAttribute("stroke-width", "2");
conn.setAttribute("opacity", "0.6");
conn.setAttribute("stroke-dasharray", "5,3");
conn.classList.add("flow-animation");
// Add to SVG temporarily
svgElement.appendChild(conn);
// Create data packet flowing along this path
createDataPacket(path, hiddenColor);
// Remove connection after delay
setTimeout(() => {
conn.remove();
}, 600);
});
});
// Activate output nodes with staggered timing
outputTargets.forEach((node, i) => {
setTimeout(() => {
// First node is the selected output (green)
if (i === 0) {
activateNode(node, selectedColor, 8, null);
// Add extra effect for selected node
const selectedGlow = document.createElementNS("http://www.w3.org/2000/svg", "circle");
selectedGlow.setAttribute("cx", node.getAttribute("cx"));
selectedGlow.setAttribute("cy", node.getAttribute("cy"));
selectedGlow.setAttribute("r", 15);
selectedGlow.setAttribute("fill", `${selectedColor}20`);
selectedGlow.setAttribute("filter", "blur(8px)");
node.parentNode.appendChild(selectedGlow);
// Animate the glow
$(selectedGlow).animate({
r: 30,
opacity: 0
}, 1000, function() {
selectedGlow.remove();
});
} else {
activateNode(node, outputColor, 6, "pulse-animation-secondary");
}
}, 400 + i * 100);
});
}, 600);
// Update model state display with the current step
const stepEl = document.createElement('div');
stepEl.className = "mb-1 pb-1 border-b dark:border-gray-700 fade-in";
let contextDescription;
let contextClass;
switch (step.contextType) {
case 'trigram':
contextDescription = "Using 2-word context";
contextClass = "text-primary-400";
break;
case 'bigram':
contextDescription = "Using 1-word context";
contextClass = "text-accent-400";
break;
case 'random':
contextDescription = "Using random selection";
contextClass = "text-secondary-400";
break;
default:
contextDescription = "Context unknown";
contextClass = "text-gray-400";
}
stepEl.innerHTML = `
<div class="text-xs"><strong class="${contextClass}">${contextDescription}:</strong>
"${step.context || 'start'}" →
<span class="text-green-500 font-bold">"${step.selected}"</span>
</div>
<div class="text-xs text-gray-500">Options: ${step.options.slice(0, 3).join(", ")}${step.options.length > 3 ? '...' : ''}</div>
`;
modelStateEl.appendChild(stepEl);
// Scroll to bottom of state display
modelStateEl.scrollTop = modelStateEl.scrollHeight;
}, delay);
delay += stepDuration;
});
return;
}
// Default animation if no steps provided - using jQuery for smoother animations
const activateLayer = (layerSelector, nextLayerSelector, connectionCount, color) => {
setTimeout(() => {
// Activate nodes in this layer
const layerNodes = document.querySelectorAll(layerSelector);
$(layerNodes).each(function() {
const node = this;
$(node).animate({
r: 7
}, 300);
node.setAttribute("fill", color);
node.setAttribute("filter", "drop-shadow(0 0 5px " + color + "80)");
// Create a pulsing ring around active node
const glow = document.createElementNS("http://www.w3.org/2000/svg", "circle");
glow.setAttribute("cx", node.getAttribute("cx"));
glow.setAttribute("cy", node.getAttribute("cy"));
glow.setAttribute("r", 10);
glow.setAttribute("fill", "none");
glow.setAttribute("stroke", color);
glow.setAttribute("stroke-width", "1");
glow.setAttribute("opacity", "0.5");
glow.style.animation = "pulse 1.5s infinite ease-out";
node.parentNode.appendChild(glow);
setTimeout(() => {
glow.remove();
$(node).animate({
r: 4
}, 300);
node.removeAttribute("filter");
if (node.classList.contains('layer-input')) {
node.setAttribute("fill", "url(#nodeGradient0)");
} else if (node.classList.contains('layer-hidden')) {
node.setAttribute("fill", "url(#nodeGradient1)");
} else if (node.classList.contains('layer-output')) {
node.setAttribute("fill", "url(#nodeGradient2)");
}
}, 800);
});
// Activate connections to next layer
if (nextLayerSelector) {
const nextLayerNodes = document.querySelectorAll(nextLayerSelector);
// Create flowing data between nodes
layerNodes.forEach(sourceNode => {
// Choose random target nodes
const targetCount = 1 + Math.floor(Math.random() * 2);
const targetIndices = [];
while (targetIndices.length < targetCount && targetIndices.length < nextLayerNodes.length) {
const idx = Math.floor(Math.random() * nextLayerNodes.length);
if (!targetIndices.includes(idx)) {
targetIndices.push(idx);
}
}
// Create data flows to these targets
targetIndices.forEach(idx => {
const targetNode = nextLayerNodes[idx];
const startX = sourceNode.getAttribute("cx");
const startY = sourceNode.getAttribute("cy");
const endX = targetNode.getAttribute("cx");
const endY = targetNode.getAttribute("cy");
// Define path for animation
const path = `M${startX},${startY} L${endX},${endY}`;
// Create active connection with color
const conn = document.createElementNS("http://www.w3.org/2000/svg", "line");
conn.setAttribute("x1", startX);
conn.setAttribute("y1", startY);
conn.setAttribute("x2", endX);
conn.setAttribute("y2", endY);
conn.setAttribute("stroke", color);
conn.setAttribute("stroke-width", "2");
conn.setAttribute("opacity", "0.6");
conn.setAttribute("stroke-dasharray", "5,3");
conn.classList.add("flow-animation");
// Add to SVG temporarily
svgElement.appendChild(conn);
// Create data packet flowing along this path
createDataPacket(path, color);
// Remove connection after delay
setTimeout(() => {
conn.remove();
}, 800);
});
});
}
}, delay);
delay += 600;
};
// Update model state with sample data
const modelStateEl = document.getElementById('model-state');
modelStateEl.innerHTML = '';
const modelState = llm.getState();
Object.entries(modelState).forEach(([ngram, nextWords]) => {
const entryEl = document.createElement('div');
entryEl.className = "mb-1 fade-in text-xs";
entryEl.innerHTML = `<strong class="text-primary-400">"${ngram}"</strong> →
<span class="text-accent-400">${nextWords.join('</span>, <span class="text-accent-400">')}</span>`;
modelStateEl.appendChild(entryEl);
});
// Animate through the layers with different colors
activateLayer('.layer-input', '.layer-hidden', 8, inputColor);
activateLayer('.layer-hidden', '.layer-output', 8, hiddenColor);
activateLayer('.layer-output', null, 0, outputColor);
}
// Tokenize input text
function visualizeTokenization(text) {
const inputViz = document.getElementById('input-viz');
// Clear previous content
inputViz.innerHTML = '';
// Tokenize the text
const tokens = llm.tokenize(text);
// Create token visualization
const tokenContainer = document.createElement('div');
tokenContainer.className = 'flex flex-wrap gap-1 p-1';
// Animate token appearance with jQuery
tokens.forEach((token, index) => {
setTimeout(() => {
const tokenEl = document.createElement('div');
tokenEl.className = 'px-2 py-1 rounded bg-primary text-white text-xs opacity-0';
tokenEl.textContent = token;
tokenContainer.appendChild(tokenEl);
// Fade in with jQuery
$(tokenEl).animate({
opacity: 1
}, 300);
// Pulse effect
setTimeout(() => {
$(tokenEl).addClass('pulse-animation');
setTimeout(() => {
$(tokenEl).removeClass('pulse-animation');
}, 800);
}, 300);
}, index * 100);
});
inputViz.appendChild(tokenContainer);
return tokens;
}
// Generate and display output
function generateAndDisplayOutput(prompt) {
const outputViz = document.getElementById('output-viz');
// Clear previous content
outputViz.innerHTML = '';
// Generate response from model
const response = llm.generate(prompt);
// Create response element
const responseEl = document.createElement('div');
responseEl.className = 'p-1';
// Create output container
const outputContainer = document.createElement('div');
outputContainer.className = 'text-gray-800 dark:text-gray-200';
// Add typing indicator
const typingIndicator = document.createElement('span');
typingIndicator.className = 'inline-block w-2 h-4 bg-primary animate-pulse ml-1';
outputContainer.appendChild(typingIndicator);
responseEl.appendChild(outputContainer);
outputViz.appendChild(responseEl);
// Animate the generation
const words = response.generated.split(' ');
let wordIndex = 0;
let outputText = '';
const wordInterval = setInterval(() => {
if (wordIndex < words.length) {
// Remove typing indicator
if (wordIndex === 0) {
typingIndicator.remove();
}
// Add the word
const wordSpan = document.createElement('span');
wordSpan.textContent = (wordIndex > 0 ? ' ' : '') + words[wordIndex];
outputContainer.appendChild(wordSpan);
// Update output text for speech
outputText += (wordIndex > 0 ? ' ' : '') + words[wordIndex];
// Animate the word briefly with jQuery
$(wordSpan).css('color', '#5D5CDE').animate({
color: document.documentElement.classList.contains('dark') ? '#e2e8f0' : '#1a202c'
}, 500);
wordIndex++;
// Scroll to bottom
outputViz.scrollTop = outputViz.scrollHeight;
} else {
clearInterval(wordInterval);
// Add the final typing indicator at the end
const finalIndicator = document.createElement('span');
finalIndicator.className = 'inline-block w-2 h-4 bg-primary animate-pulse ml-1';
outputContainer.appendChild(finalIndicator);
// Remove the final indicator after a delay
setTimeout(() => {
finalIndicator.remove();
// Speak the output if voice is enabled
if (speechSynth && speechSynth.isVoiceEnabled) {
speechSynth.speak(outputText);
}
}, 1000);
}
}, 200);
// Return the generation steps for network animation
return response.steps;
}
// Process the prompt
function processPrompt() {
const promptInput = document.getElementById('prompt-input');
const text = promptInput.value.trim();
if (text === '') {
alert('Please enter a prompt');
return;
}
// Stop any current speech synthesis
if (speechSynth) {
speechSynth.stop();
}
// Stop any active speech recognition
if (speechRecognition && speechRecognition.isListening) {
speechRecognition.stop();
}
// Visualize tokenization
visualizeTokenization(text);
// Animate neural network (generic animation first)
animateNetwork();
// Generate output after a delay
setTimeout(() => {
const steps = generateAndDisplayOutput(text);
// Animate network with specific steps
setTimeout(() => {
animateNetwork(steps);
}, 600);
}, 1000);
}
// Process web content
async function processWebContent() {
const urlInput = document.getElementById('url-input');
const url = urlInput.value.trim();
const webPreview = document.getElementById('web-preview');
if (!url || !url.startsWith('http')) {
alert('Please enter a valid URL');
return;
}
// Show loading state
webPreview.classList.remove('hidden');
webPreview.innerHTML = '<p class="text-center py-4"><span class="inline-block w-4 h-4 border-2 border-primary border-t-transparent rounded-full animate-spin mr-2"></span> Fetching content...</p>';
try {
// Attempt to fetch the URL's content
const response = await fetch(url);
const text = await response.text();
// Extract text content from HTML
const parser = new DOMParser();
const doc = parser.parseFromString(text, 'text/html');
// Remove script and style elements
const scripts = doc.querySelectorAll('script, style, svg, iframe, noscript');
scripts.forEach(el => el.remove());
// Get meaningful text from the document
const bodyText = doc.body.textContent || "";
const cleanText = bodyText
.replace(/\s+/g, ' ')
.replace(/\n+/g, ' ')
.trim();
// Display a preview
const textPreview = cleanText.slice(0, 300) + (cleanText.length > 300 ? '...' : '');
webPreview.innerHTML = `
<div class="text-sm">
<p class="font-medium mb-2">Content Preview:</p>
<p class="text-gray-700 dark:text-gray-300">${textPreview}</p>
</div>
<div class="mt-3 flex justify-between">
<span class="text-xs text-gray-500">${cleanText.length} characters</span>
<button id="train-web-btn" class="px-3 py-1 bg-primary hover:bg-opacity-90 text-white rounded-lg text-xs transition-all shadow-md">
Train Model on Content
</button>
</div>
`;
// Store the full text for training
webPreview.dataset.fullText = cleanText;
// Add event listener to train button
document.getElementById('train-web-btn').addEventListener('click', () => {
trainOnWebContent(url, cleanText);
});
} catch (error) {
console.error("Error fetching URL:", error);
webPreview.innerHTML = `
<div class="bg-red-100 dark:bg-red-900 border-l-4 border-red-500 text-red-700 dark:text-red-300 p-3">
<p>Error fetching content. This could be due to CORS restrictions or the domain not being allowed by the CSP.</p>
<p class="text-xs mt-1">Try a different URL from an allowed domain.</p>
</div>
`;
}
}
// Train on web content
function trainOnWebContent(url, text) {
if (!text || text.length < 10) {
alert('Not enough content to train on');
return;
}
// Extract domain for source name
let domain;
try {
domain = new URL(url).hostname;
} catch (e) {
domain = url;
}
// Train the model
llm.train(text, {
type: 'web',
name: `Web: ${domain}`
});
// Show success message
const webPreview = document.getElementById('web-preview');
webPreview.innerHTML += `
<div class="mt-2 bg-green-100 dark:bg-green-900 border-l-4 border-green-500 text-green-700 dark:text-green-300 p-2 text-sm">
Model trained successfully on content from ${domain}!
</div>
`;
// Animate the neural network
animateNetwork();
}
// Handle text file upload
function handleFileUpload(e) {
const file = e.target.files[0];
if (!file) {
return;
}
// Check if it's a text file
if (!file.type.match('text.*') && !file.name.match(/\.(txt|md|text)$/i)) {
alert('Please select a text file (.txt, .md, etc.)');
return;
}
// Read the file
const reader = new FileReader();
reader.onload = function(event) {
const text = event.target.result;
// Train the model on file content
trainOnFileContent(file.name, text);
};
reader.onerror = function() {
alert('Error reading file. Please try another file.');
};
reader.readAsText(file);
}
// Train on file content
function trainOnFileContent(fileName, text) {
if (!text || text.length < 10) {
alert('Not enough content to train on');
return;
}
// Train the model
llm.train(text, {
type: 'file',
name: `File: ${fileName}`
});
// Show success message
const feedback = $('<div>')
.text(`Model trained successfully on file: ${fileName}!`)
.addClass('text-green-600 dark:text-green-400 text-sm mt-2 p-2 bg-green-100 dark:bg-green-900 rounded')
.hide()
.appendTo('#panel-data')
.slideDown();
// Fade out and remove the feedback
setTimeout(() => {
feedback.slideUp(function() {
$(this).remove();
});
}, 2000);
// Animate the neural network
animateNetwork();
// Switch to the sources tab to show the new data source
setTimeout(() => {
switchTab('sources');
}, 1000);
}
// Add training data to the model
function addTrainingData() {
const trainingInput = document.getElementById('training-input');
const text = trainingInput.value.trim();
if (text === '') {
alert('Please enter some text to add to the model');
return;
}
// Train the model on the new data
llm.train(text, {
type: 'training',
name: 'Manual Training Entry'
});
// Clear the input
trainingInput.value = '';
// Show feedback with jQuery animation
const feedback = $('<div>')
.text('Training data added successfully!')
.addClass('text-green-600 dark:text-green-400 text-sm mt-2 p-2 bg-green-100 dark:bg-green-900 rounded')
.hide()
.appendTo('#panel-data')
.slideDown();
// Fade out and remove the feedback
setTimeout(() => {
feedback.slideUp(function() {
$(this).remove();
});
}, 2000);
// Animate the neural network
animateNetwork();
}
// Reset the model
function resetModel() {
if (confirm('Are you sure you want to reset the model? All training data will be lost.')) {
llm.reset();
// Show feedback with jQuery
const feedback = $('<div>')
.text('Model has been reset to default state.')
.addClass('text-blue-600 dark:text-blue-400 text-sm mt-2 p-2 bg-blue-100 dark:bg-blue-900 rounded')
.hide()
.appendTo('#panel-data')
.slideDown();
// Fade out and remove the feedback
setTimeout(() => {
feedback.slideUp(function() {
$(this).remove();
});
}, 2000);
// Update visualization
animateNetwork();
}
}
// Export model data
function exportModelData() {
llm.exportModel();
}
// Import model data
function importModelData(e) {
const file = e.target.files[0];
if (!file) return;
// Must be JSON file
if (file.type !== 'application/json' && !file.name.endsWith('.json')) {
alert('Please select a JSON file.');
return;
}
const reader = new FileReader();
reader.onload = function(event) {
const jsonData = event.target.result;
const success = llm.importModel(jsonData);
if (success) {
// Show success message
const feedback = $('<div>')
.text('Model imported successfully!')
.addClass('text-green-600 dark:text-green-400 text-sm p-2 bg-green-100 dark:bg-green-900 rounded')
.css({
position: 'fixed',
bottom: '20px',
left: '50%',
transform: 'translateX(-50%)',
zIndex: '1000',
padding: '10px 20px',
boxShadow: '0 4px 6px rgba(0,0,0,0.1)',
borderRadius: '8px'
})
.hide()
.appendTo('body')
.fadeIn();
// Fade out and remove the feedback
setTimeout(() => {
feedback.fadeOut(function() {
$(this).remove();
});
}, 3000);
// Update visualization and switch to sources tab
animateNetwork();
switchTab('sources');
} else {
alert('Failed to import model. The file may be corrupted or in the wrong format.');
}
// Clear the file input for future imports
document.getElementById('import-file').value = '';
};
reader.readAsText(file);
}
// Initialize help system
function initializeHelpSystem() {
// Toggle help overlay
document.getElementById('help-toggle').addEventListener('click', () => {
document.getElementById('help-overlay').classList.remove('hidden');
});
document.getElementById('help-close').addEventListener('click', () => {
document.getElementById('help-overlay').classList.add('hidden');
});
// Help navigation
document.querySelectorAll('.help-step').forEach(step => {
step.addEventListener('click', () => {
switchHelpSection(step.dataset.step);
});
});
// Next/prev navigation
document.getElementById('next-help').addEventListener('click', () => {
const currentIndex = helpSections.indexOf(currentHelpSection);
const nextIndex = (currentIndex + 1) % helpSections.length;
switchHelpSection(helpSections[nextIndex]);
});
document.getElementById('prev-help').addEventListener('click', () => {
const currentIndex = helpSections.indexOf(currentHelpSection);
const prevIndex = (currentIndex - 1 + helpSections.length) % helpSections.length;
switchHelpSection(helpSections[prevIndex]);
});
// Keyboard shortcuts for help
document.addEventListener('keydown', e => {
// ESC to close help
if (e.key === 'Escape') {
document.getElementById('help-overlay').classList.add('hidden');
}
// ? to open help
if (e.key === '?' && !document.getElementById('help-overlay').classList.contains('hidden')) {
document.getElementById('help-overlay').classList.add('hidden');
} else if (e.key === '?') {
document.getElementById('help-overlay').classList.remove('hidden');
}
});
}
// Initialize the visualization
window.addEventListener('load', () => {
// Initialize 2D neural network visualization
initializeNeuralNetwork();
animateNetwork();
// Initialize speech synthesis controller
if (window.speechSynthesis) {
speechSynth = new SpeechController();
}
// Initialize speech recognition controller
speechRecognition = new SpeechRecognitionController();
// Initialize image generator
imageGenerator = new ImageGenerator();
// Initialize music generator
musicGenerator = new MusicGenerator();
// Initialize help system
initializeHelpSystem();
// Handle window resize
window.addEventListener('resize', () => {
initializeNeuralNetwork();
});
// Tab switching listeners
document.getElementById('tab-prompt').addEventListener('click', () => switchTab('prompt'));
document.getElementById('tab-create').addEventListener('click', () => switchTab('create'));
document.getElementById('tab-web').addEventListener('click', () => switchTab('web'));
document.getElementById('tab-data').addEventListener('click', () => switchTab('data'));
document.getElementById('tab-sources').addEventListener('click', () => switchTab('sources'));
// Creation type switching listeners
document.getElementById('create-type-image').addEventListener('click', () => switchCreationType('image'));
document.getElementById('create-type-music').addEventListener('click', () => switchCreationType('music'));
// Submit button click
document.getElementById('submit-btn').addEventListener('click', processPrompt);
// Enter key press
document.getElementById('prompt-input').addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
processPrompt();
}
});
// Web URL fetch button
document.getElementById('fetch-btn').addEventListener('click', processWebContent);
// URL input enter key
document.getElementById('url-input').addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
processWebContent();
}
});
// File upload handler
document.getElementById('file-upload').addEventListener('change', handleFileUpload);
// Training button click
document.getElementById('train-btn').addEventListener('click', addTrainingData);
// Reset button click
document.getElementById('reset-btn').addEventListener('click', resetModel);
// Export model button
document.getElementById('export-btn').addEventListener('click', exportModelData);
// Import model file handler
document.getElementById('import-file').addEventListener('change', importModelData);
// Set seed for music generator with same value as image generator
musicGenerator.seed = imageGenerator.seed;
// Image seed change should update music seed too
document.getElementById('image-seed').addEventListener('change', (e) => {
musicGenerator.seed = parseInt(e.target.value);
});
// Add keyboard shortcut for microphone
document.addEventListener('keydown', (e) => {
// Ctrl+M to toggle microphone
if (e.ctrlKey && e.key === 'm') {
if (speechRecognition) {
if (speechRecognition.isListening) {
speechRecognition.stop();
} else {
speechRecognition.start();
}
e.preventDefault();
}
}
// Space to play/pause music when in music tab
if (e.code === 'Space' && !document.getElementById('panel-create').classList.contains('hidden') &&
!document.getElementById('creation-music').classList.contains('hidden') &&
!document.activeElement.tagName.match(/INPUT|TEXTAREA/)) {
if (musicGenerator.isPlaying) {
musicGenerator.stopMusic();
} else {
musicGenerator.playMusic();
}
e.preventDefault();
}
// Ctrl+S to save image/music
if (e.ctrlKey && e.key === 's' && !document.getElementById('panel-create').classList.contains('hidden')) {
if (!document.getElementById('creation-image').classList.contains('hidden')) {
if (imageGenerator.hasImage) {
imageGenerator.downloadImage();
e.preventDefault();
}
}
}
});
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment