llm-playground / js /main.js
Debashis
Initial commit: LLM Playground with interactive demos
ba5b708
/**
* LLM Playground - Interactive JavaScript
* Educational demonstrations of LLM concepts
*/
// ========================================
// Theme Management
// ========================================
class ThemeManager {
constructor() {
this.themeToggle = document.getElementById('themeToggle');
this.currentTheme = localStorage.getItem('theme') || 'light';
this.init();
}
init() {
this.applyTheme(this.currentTheme);
this.themeToggle?.addEventListener('click', () => this.toggle());
}
applyTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
const icon = this.themeToggle?.querySelector('i');
if (icon) {
icon.className = theme === 'dark' ? 'fas fa-sun' : 'fas fa-moon';
}
localStorage.setItem('theme', theme);
}
toggle() {
this.currentTheme = this.currentTheme === 'dark' ? 'light' : 'dark';
this.applyTheme(this.currentTheme);
}
}
// ========================================
// Navigation
// ========================================
class Navigation {
constructor() {
this.navLinks = document.querySelectorAll('.nav-link');
this.sections = document.querySelectorAll('.section');
this.init();
}
init() {
// Smooth scroll
this.navLinks.forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
const targetId = link.getAttribute('href').slice(1);
const target = document.getElementById(targetId);
if (target) {
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
});
});
// Active link on scroll
window.addEventListener('scroll', () => this.updateActiveLink());
}
updateActiveLink() {
const scrollPos = window.scrollY + 100;
this.sections.forEach(section => {
const top = section.offsetTop;
const bottom = top + section.offsetHeight;
const id = section.getAttribute('id');
if (scrollPos >= top && scrollPos < bottom) {
this.navLinks.forEach(link => {
link.classList.remove('active');
if (link.getAttribute('href') === `#${id}`) {
link.classList.add('active');
}
});
}
});
}
}
// ========================================
// Section Animations
// ========================================
class SectionAnimator {
constructor() {
this.sections = document.querySelectorAll('.section');
this.init();
}
init() {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
}
});
}, { threshold: 0.1 });
this.sections.forEach(section => observer.observe(section));
}
}
// ========================================
// Tab System
// ========================================
class TabSystem {
constructor() {
this.tabButtons = document.querySelectorAll('.tab-btn');
this.init();
}
init() {
this.tabButtons.forEach(btn => {
btn.addEventListener('click', () => {
const tabId = btn.dataset.tab;
const parent = btn.closest('.subsection');
// Update buttons
parent.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
// Update content
parent.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
parent.querySelector(`#${tabId}`).classList.add('active');
});
});
}
}
// ========================================
// Data Cleaning Demo
// ========================================
class DataCleaningDemo {
constructor() {
this.input = document.getElementById('cleaningInput');
this.output = document.getElementById('cleaningOutput');
this.cleanBtn = document.getElementById('cleanTextBtn');
this.checkboxes = {
removeHtml: document.getElementById('removeHtml'),
normalizeSpaces: document.getElementById('normalizeSpaces'),
removeUrls: document.getElementById('removeUrls'),
removeEmails: document.getElementById('removeEmails'),
removeDuplicates: document.getElementById('removeDuplicates')
};
this.init();
}
init() {
this.cleanBtn?.addEventListener('click', () => this.clean());
}
clean() {
let text = this.input.value;
if (this.checkboxes.removeHtml?.checked) {
text = text.replace(/<[^>]*>/g, '');
}
if (this.checkboxes.removeUrls?.checked) {
text = text.replace(/https?:\/\/[^\s]+/g, '');
}
if (this.checkboxes.removeEmails?.checked) {
text = text.replace(/[\w.-]+@[\w.-]+\.\w+/g, '');
}
if (this.checkboxes.normalizeSpaces?.checked) {
text = text.replace(/\s+/g, ' ').trim();
}
if (this.checkboxes.removeDuplicates?.checked) {
const lines = text.split('\n');
const unique = [...new Set(lines)];
text = unique.join('\n');
}
this.output.value = text;
}
}
// ========================================
// Tokenization Demo
// ========================================
class TokenizationDemo {
constructor() {
this.input = document.getElementById('tokenizeInput');
this.output = document.getElementById('tokenOutput');
this.type = document.getElementById('tokenizerType');
this.tokenizeBtn = document.getElementById('tokenizeBtn');
this.tokenCount = document.getElementById('tokenCount');
this.vocabSize = document.getElementById('vocabSize');
// Simple BPE-like vocabulary
this.bpeVocab = [
'the', 'ing', 'tion', 'ed', 'er', 'es', 'en', 'al', 'ly', 're',
'an', 'or', 'is', 'it', 'at', 'on', 'as', 'ar', 'le', 'se',
'Hello', 'how', 'are', 'you', 'today', 'what', 'can', 'help',
'machine', 'learn', 'artificial', 'intelligence', 'model', 'data'
];
this.colors = [
'#6366f1', '#8b5cf6', '#a855f7', '#d946ef', '#ec4899',
'#f43f5e', '#f97316', '#eab308', '#84cc16', '#22c55e',
'#14b8a6', '#06b6d4', '#0ea5e9', '#3b82f6'
];
this.init();
}
init() {
this.tokenizeBtn?.addEventListener('click', () => this.tokenize());
this.input?.addEventListener('keypress', (e) => {
if (e.key === 'Enter') this.tokenize();
});
}
tokenize() {
const text = this.input.value;
const type = this.type.value;
let tokens = [];
switch (type) {
case 'word':
tokens = this.wordTokenize(text);
break;
case 'character':
tokens = this.charTokenize(text);
break;
case 'bpe':
tokens = this.bpeTokenize(text);
break;
}
this.displayTokens(tokens);
}
wordTokenize(text) {
return text.match(/\w+|[^\w\s]/g) || [];
}
charTokenize(text) {
return text.split('');
}
bpeTokenize(text) {
let tokens = [];
const words = text.split(/\s+/);
words.forEach(word => {
let remaining = word;
while (remaining.length > 0) {
let matched = false;
// Try to match longest subword from vocabulary
for (let len = remaining.length; len > 0; len--) {
const subword = remaining.slice(0, len);
if (this.bpeVocab.includes(subword) || len === 1) {
tokens.push(subword);
remaining = remaining.slice(len);
matched = true;
break;
}
}
if (!matched) {
tokens.push(remaining[0]);
remaining = remaining.slice(1);
}
}
tokens.push(' ');
});
// Remove trailing space
if (tokens[tokens.length - 1] === ' ') {
tokens.pop();
}
return tokens;
}
displayTokens(tokens) {
this.output.innerHTML = '';
const uniqueTokens = new Set();
tokens.forEach((token, i) => {
uniqueTokens.add(token);
const span = document.createElement('span');
span.className = 'token';
span.textContent = token === ' ' ? '▁' : token;
span.style.backgroundColor = this.colors[i % this.colors.length] + '30';
span.style.borderColor = this.colors[i % this.colors.length];
span.title = `Token ID: ${i}`;
this.output.appendChild(span);
});
this.tokenCount.textContent = tokens.length;
this.vocabSize.textContent = uniqueTokens.size;
}
}
// ========================================
// Attention Visualization
// ========================================
class AttentionVisualization {
constructor() {
this.input = document.getElementById('attentionInput');
this.showBtn = document.getElementById('showAttentionBtn');
this.matrix = document.getElementById('attentionMatrix');
this.init();
}
init() {
this.showBtn?.addEventListener('click', () => this.visualize());
}
visualize() {
const text = this.input.value;
const words = text.split(/\s+/);
// Generate random attention scores (simulated)
const attentionScores = this.generateAttentionScores(words.length);
this.renderMatrix(words, attentionScores);
}
generateAttentionScores(size) {
const scores = [];
for (let i = 0; i < size; i++) {
const row = [];
for (let j = 0; j < size; j++) {
// Generate realistic-looking attention patterns
let score = Math.random() * 0.5;
// Higher attention on self
if (i === j) score += 0.3;
// Higher attention on nearby words
if (Math.abs(i - j) === 1) score += 0.2;
// Simulate attention to important positions (first, last)
if (j === 0 || j === size - 1) score += 0.1;
row.push(Math.min(score, 1));
}
// Normalize row to sum to 1
const sum = row.reduce((a, b) => a + b, 0);
scores.push(row.map(s => s / sum));
}
return scores;
}
renderMatrix(words, scores) {
this.matrix.innerHTML = '';
this.matrix.style.gridTemplateColumns = `repeat(${words.length + 1}, auto)`;
// Header row
const headerRow = document.createElement('div');
headerRow.className = 'attention-row';
headerRow.appendChild(this.createCell('', 'header'));
words.forEach(word => {
headerRow.appendChild(this.createCell(word, 'header'));
});
this.matrix.appendChild(headerRow);
// Data rows
words.forEach((word, i) => {
const row = document.createElement('div');
row.className = 'attention-row';
row.appendChild(this.createCell(word, 'header'));
scores[i].forEach(score => {
const cell = this.createCell(score.toFixed(2), 'value');
cell.style.backgroundColor = this.getHeatmapColor(score);
row.appendChild(cell);
});
this.matrix.appendChild(row);
});
}
createCell(content, type) {
const cell = document.createElement('div');
cell.className = `attention-cell ${type}`;
cell.textContent = content;
return cell;
}
getHeatmapColor(value) {
// Blue to red gradient
const r = Math.round(255 * value);
const b = Math.round(255 * (1 - value));
return `rgba(${r}, 50, ${b}, 0.8)`;
}
}
// ========================================
// Text Generation Demo
// ========================================
class TextGenerationDemo {
constructor() {
this.prompt = document.getElementById('generationPrompt');
this.method = document.getElementById('generationMethod');
this.tempSlider = document.getElementById('tempSlider');
this.tempValue = document.getElementById('tempValue');
this.maxTokensSlider = document.getElementById('maxTokensSlider');
this.maxTokensValue = document.getElementById('maxTokensValue');
this.generateBtn = document.getElementById('generateBtn');
this.output = document.getElementById('generationOutput');
this.probViz = document.getElementById('probabilityViz');
// Sample vocabulary for generation
this.vocabulary = {
'The future of artificial intelligence is': [
'bright', 'uncertain', 'promising', 'complex', 'exciting', 'challenging'
],
'bright': ['and', 'with', 'as', ',', '.', 'because'],
'uncertain': ['but', 'yet', 'however', ',', '.', 'given'],
'promising': ['with', 'as', 'and', ',', '.', 'for'],
'and': ['it', 'we', 'the', 'this', 'many', 'there'],
'with': ['many', 'new', 'great', 'significant', 'potential', 'numerous'],
'default': ['the', 'a', 'and', 'to', 'of', 'is', 'in', 'that', 'it', 'for']
};
this.init();
}
init() {
this.tempSlider?.addEventListener('input', () => {
this.tempValue.textContent = (this.tempSlider.value / 100).toFixed(1);
});
this.maxTokensSlider?.addEventListener('input', () => {
this.maxTokensValue.textContent = this.maxTokensSlider.value;
});
this.generateBtn?.addEventListener('click', () => this.generate());
}
generate() {
const promptText = this.prompt.value;
const method = this.method.value;
const temperature = this.tempSlider.value / 100;
const maxTokens = parseInt(this.maxTokensSlider.value);
let generated = promptText;
this.output.innerHTML = '';
this.probViz.innerHTML = '';
// Animate generation
this.animateGeneration(generated, method, temperature, maxTokens);
}
async animateGeneration(text, method, temperature, maxTokens) {
this.output.textContent = text;
for (let i = 0; i < maxTokens; i++) {
await this.delay(100);
const lastWords = text.split(' ').slice(-5).join(' ');
const candidates = this.vocabulary[lastWords] ||
this.vocabulary[text.split(' ').pop()] ||
this.vocabulary['default'];
const probabilities = this.calculateProbabilities(candidates, temperature);
const nextToken = this.selectToken(candidates, probabilities, method);
text += ' ' + nextToken;
this.output.textContent = text;
// Show probability visualization for last few tokens
if (i < 5) {
this.showProbabilities(candidates, probabilities, nextToken);
}
if (nextToken === '.') break;
}
}
calculateProbabilities(candidates, temperature) {
const logits = candidates.map((_, i) => 1 / (i + 1)); // Simulated logits
const scaled = logits.map(l => Math.exp(l / Math.max(temperature, 0.1)));
const sum = scaled.reduce((a, b) => a + b, 0);
return scaled.map(s => s / sum);
}
selectToken(candidates, probabilities, method) {
switch (method) {
case 'greedy':
return candidates[0];
case 'beam':
// Simplified beam - just take top 3 and randomly select
return candidates[Math.floor(Math.random() * Math.min(3, candidates.length))];
case 'topk':
const k = 5;
const topK = candidates.slice(0, k);
return topK[Math.floor(Math.random() * topK.length)];
case 'topp':
let cumProb = 0;
const p = 0.9;
for (let i = 0; i < candidates.length; i++) {
cumProb += probabilities[i];
if (cumProb >= p || Math.random() < probabilities[i] * 2) {
return candidates[i];
}
}
return candidates[0];
default:
return candidates[0];
}
}
showProbabilities(candidates, probabilities, selected) {
const container = document.createElement('div');
container.style.display = 'flex';
container.style.gap = '4px';
container.style.marginBottom = '4px';
candidates.slice(0, 5).forEach((token, i) => {
const span = document.createElement('span');
span.className = 'token';
span.textContent = `${token} (${(probabilities[i] * 100).toFixed(0)}%)`;
span.style.fontSize = '0.75rem';
if (token === selected) {
span.style.backgroundColor = '#6366f150';
span.style.borderColor = '#6366f1';
}
container.appendChild(span);
});
this.probViz.appendChild(container);
}
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
// ========================================
// Parameter Sliders
// ========================================
class ParameterSliders {
constructor() {
this.topkSlider = document.getElementById('topkSlider');
this.topkValue = document.getElementById('topkValue');
this.toppSlider = document.getElementById('toppSlider');
this.toppValue = document.getElementById('toppValue');
this.init();
}
init() {
this.topkSlider?.addEventListener('input', () => {
this.topkValue.textContent = this.topkSlider.value;
});
this.toppSlider?.addEventListener('input', () => {
this.toppValue.textContent = (this.toppSlider.value / 100).toFixed(2);
});
}
}
// ========================================
// RLHF Demo
// ========================================
class RLHFDemo {
constructor() {
this.preferBtns = document.querySelectorAll('.prefer-btn');
this.rewardScores = document.getElementById('rewardScores');
this.scoreBarA = document.getElementById('scoreBarA');
this.scoreBarB = document.getElementById('scoreBarB');
this.scoreValueA = document.getElementById('scoreValueA');
this.scoreValueB = document.getElementById('scoreValueB');
this.init();
}
init() {
this.preferBtns.forEach(btn => {
btn.addEventListener('click', () => this.showReward(btn.dataset.choice));
});
}
showReward(choice) {
// Simulate reward model scores
const scoreA = 0.85;
const scoreB = 0.42;
// Highlight selected option
document.querySelectorAll('.response-option').forEach(opt => {
opt.classList.remove('selected');
});
document.getElementById(`response${choice}`).classList.add('selected');
// Show reward scores
this.rewardScores.style.display = 'block';
// Animate bars
setTimeout(() => {
this.scoreBarA.style.width = `${scoreA * 100}%`;
this.scoreBarB.style.width = `${scoreB * 100}%`;
}, 100);
}
}
// ========================================
// Benchmark Chart
// ========================================
class BenchmarkChart {
constructor() {
this.select = document.getElementById('benchmarkSelect');
this.chart = document.getElementById('benchmarkChart');
this.data = {
mmlu: {
'GPT-4': 86.4,
'Claude 3': 85.8,
'Gemini': 83.7,
'LLaMA 3': 79.5,
'Mistral': 73.2
},
hellaswag: {
'GPT-4': 95.3,
'Claude 3': 94.1,
'Gemini': 93.8,
'LLaMA 3': 91.2,
'Mistral': 88.7
},
humaneval: {
'GPT-4': 87.0,
'Claude 3': 84.9,
'Gemini': 74.4,
'LLaMA 3': 72.6,
'Mistral': 68.2
},
gsm8k: {
'GPT-4': 92.0,
'Claude 3': 88.1,
'Gemini': 86.5,
'LLaMA 3': 82.3,
'Mistral': 75.4
}
};
this.init();
}
init() {
this.select?.addEventListener('change', () => this.render());
this.render();
}
render() {
const benchmark = this.select?.value || 'mmlu';
const data = this.data[benchmark];
this.chart.innerHTML = '';
const maxValue = Math.max(...Object.values(data));
Object.entries(data).forEach(([model, score]) => {
const bar = document.createElement('div');
bar.className = 'chart-bar';
const fill = document.createElement('div');
fill.className = 'chart-bar-fill';
fill.style.height = `${(score / maxValue) * 220}px`;
const label = document.createElement('div');
label.className = 'chart-bar-label';
label.textContent = model;
const value = document.createElement('div');
value.className = 'chart-bar-value';
value.textContent = score.toFixed(1);
bar.appendChild(value);
bar.appendChild(fill);
bar.appendChild(label);
this.chart.appendChild(bar);
});
}
}
// ========================================
// Chat Demo
// ========================================
class ChatDemo {
constructor() {
this.systemPrompt = document.getElementById('systemPrompt');
this.chatWindow = document.getElementById('chatWindow');
this.chatInput = document.getElementById('chatInput');
this.sendBtn = document.getElementById('sendMessageBtn');
this.responses = {
'what is an llm': 'An LLM (Large Language Model) is an AI system trained on massive amounts of text data. It learns patterns in language and can generate human-like text, answer questions, and perform various language tasks.',
'transformer': 'Transformers are the architecture behind modern LLMs. They use self-attention mechanisms to process input sequences in parallel, allowing them to understand relationships between all parts of the input simultaneously.',
'attention': 'Attention is a mechanism that allows models to focus on relevant parts of the input when producing output. Self-attention computes relationships between all positions in a sequence using Query, Key, and Value projections.',
'tokenization': 'Tokenization breaks text into smaller units called tokens. Common methods include word-level, character-level, and subword tokenization (like BPE). This allows models to work with a manageable vocabulary size.',
'rlhf': 'RLHF (Reinforcement Learning from Human Feedback) aligns LLMs with human preferences. It involves collecting human comparisons, training a reward model, and optimizing the LLM using reinforcement learning (typically PPO).',
'fine-tuning': 'Fine-tuning adapts a pre-trained model to specific tasks or domains. Supervised Fine-Tuning (SFT) uses labeled examples, while RLHF further aligns the model with human preferences.',
'default': 'That\'s an interesting question about LLMs! The field is evolving rapidly. Key concepts include pre-training on large datasets, transformer architecture, tokenization, and alignment techniques like RLHF. What specific aspect would you like to explore?'
};
this.init();
}
init() {
this.sendBtn?.addEventListener('click', () => this.sendMessage());
this.chatInput?.addEventListener('keypress', (e) => {
if (e.key === 'Enter') this.sendMessage();
});
}
sendMessage() {
const message = this.chatInput.value.trim();
if (!message) return;
// Add user message
this.addMessage(message, 'user');
this.chatInput.value = '';
// Simulate thinking delay
setTimeout(() => {
const response = this.getResponse(message);
this.addMessage(response, 'assistant');
}, 500 + Math.random() * 500);
}
addMessage(content, role) {
const messageDiv = document.createElement('div');
messageDiv.className = `chat-message ${role}`;
const avatar = document.createElement('div');
avatar.className = 'message-avatar';
avatar.innerHTML = role === 'user' ? '<i class="fas fa-user"></i>' : '<i class="fas fa-robot"></i>';
const contentDiv = document.createElement('div');
contentDiv.className = 'message-content';
contentDiv.textContent = content;
messageDiv.appendChild(avatar);
messageDiv.appendChild(contentDiv);
this.chatWindow.appendChild(messageDiv);
this.chatWindow.scrollTop = this.chatWindow.scrollHeight;
}
getResponse(message) {
const lower = message.toLowerCase();
for (const [key, response] of Object.entries(this.responses)) {
if (lower.includes(key)) {
return response;
}
}
return this.responses['default'];
}
}
// ========================================
// Neural Network Animation
// ========================================
class NeuralNetworkAnimation {
constructor() {
this.canvas = document.getElementById('neuralNetworkCanvas');
this.nodes = [];
this.connections = [];
this.init();
}
init() {
if (!this.canvas) return;
// Create SVG container
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('width', '100%');
svg.setAttribute('height', '100%');
svg.setAttribute('viewBox', '0 0 400 400');
this.canvas.appendChild(svg);
// Create network structure
const layers = [4, 6, 6, 4];
let x = 60;
layers.forEach((nodeCount, layerIndex) => {
const layerNodes = [];
const yStep = 300 / (nodeCount + 1);
for (let i = 0; i < nodeCount; i++) {
const y = 50 + yStep * (i + 1);
const node = this.createNode(svg, x, y);
layerNodes.push({ element: node, x, y });
}
// Connect to previous layer
if (this.nodes.length > 0) {
const prevLayer = this.nodes[this.nodes.length - 1];
prevLayer.forEach(prevNode => {
layerNodes.forEach(currNode => {
const line = this.createConnection(svg, prevNode, currNode);
this.connections.push(line);
});
});
}
this.nodes.push(layerNodes);
x += 100;
});
// Animate
this.animate();
}
createNode(svg, x, y) {
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
circle.setAttribute('cx', x);
circle.setAttribute('cy', y);
circle.setAttribute('r', '8');
circle.setAttribute('fill', '#6366f1');
circle.setAttribute('opacity', '0.7');
svg.appendChild(circle);
return circle;
}
createConnection(svg, from, to) {
const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
line.setAttribute('x1', from.x);
line.setAttribute('y1', from.y);
line.setAttribute('x2', to.x);
line.setAttribute('y2', to.y);
line.setAttribute('stroke', '#6366f1');
line.setAttribute('stroke-width', '1');
line.setAttribute('opacity', '0.2');
svg.insertBefore(line, svg.firstChild);
return line;
}
animate() {
let index = 0;
setInterval(() => {
// Pulse random connections
const connection = this.connections[index % this.connections.length];
connection.setAttribute('opacity', '0.8');
connection.setAttribute('stroke-width', '2');
setTimeout(() => {
connection.setAttribute('opacity', '0.2');
connection.setAttribute('stroke-width', '1');
}, 200);
index++;
}, 50);
}
}
// ========================================
// Playground Cards
// ========================================
class PlaygroundCards {
constructor() {
this.cards = document.querySelectorAll('.playground-card');
this.init();
}
init() {
this.cards.forEach(card => {
card.addEventListener('click', () => {
const demo = card.dataset.demo;
this.scrollToDemo(demo);
});
});
}
scrollToDemo(demo) {
const demoMap = {
'tokenizer': 'tokenizeInput',
'attention': 'attentionInput',
'generation': 'generationPrompt',
'embeddings': 'overview'
};
const target = document.getElementById(demoMap[demo]);
if (target) {
target.scrollIntoView({ behavior: 'smooth', block: 'center' });
target.focus();
}
}
}
// ========================================
// Initialize Everything
// ========================================
document.addEventListener('DOMContentLoaded', () => {
// Core functionality
new ThemeManager();
new Navigation();
new SectionAnimator();
new TabSystem();
// Interactive demos
new DataCleaningDemo();
new TokenizationDemo();
new AttentionVisualization();
new TextGenerationDemo();
new ParameterSliders();
new RLHFDemo();
new BenchmarkChart();
new ChatDemo();
// Visual elements
new NeuralNetworkAnimation();
new PlaygroundCards();
console.log('🚀 LLM Playground initialized!');
});