Skip to content
LAM
Read Home Blog
Make Projects HTML Tools Games
Touch grass Notes Resume Links
Home Blog HTML Projects
Tools Games Notes Resume Links
Back Interactive Series Visualizer Math
Download Open
Show description 720 chars · Math

Interactive Series Visualizer

Interactive Series Visualizer









Series Convergence Visualizer

Watch sums build to see convergence in action.







Converges

The infinite sum approaches a single, finite number. On the graph, the line will flatten out and approach a horizontal asymptote.




Diverges

The infinite sum does not settle on a single value. It might shoot off to infinity (∞ or -∞) or oscillate without approaching a limit.











Controls



Series Type

Geometric Series
p-Series
Alternating p-Series
Ratio Test (Factorial)
Ratio Test (Exponential)









Start
Reset





Series Info


Formula:

Terms Added (n): 0

Partial Sum (S_n): 0.00

Verdict: Press Start

Interactive Series Visualizer

17,949 bytes · HTML source
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Interactive Series Visualizer</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
    <style>
        body {
            font-family: 'Inter', sans-serif;
            background-color: #030712; /* gray-950 */
            color: #d1d5db; /* gray-300 */
        }
        /* Custom Scrollbar */
        ::-webkit-scrollbar {
            width: 8px;
        }
        ::-webkit-scrollbar-track {
            background: #111827; /* gray-900 */
        }
        ::-webkit-scrollbar-thumb {
            background: #4b5563; /* gray-600 */
            border-radius: 4px;
        }
        ::-webkit-scrollbar-thumb:hover {
            background: #6b7280; /* gray-500 */
        }
        .control-panel, .info-card {
            background-color: #111827; /* gray-900 */
            border: 1px solid #374151; /* gray-700 */
            border-radius: 0.75rem;
            padding: 1.5rem;
        }
        .slider-container {
            margin-top: 1rem;
        }
        input[type="range"] {
            -webkit-appearance: none;
            appearance: none;
            width: 100%;
            height: 6px;
            background: #374151; /* gray-700 */
            border-radius: 3px;
            outline: none;
        }
        input[type="range"]::-webkit-slider-thumb {
            -webkit-appearance: none;
            appearance: none;
            width: 20px;
            height: 20px;
            background: #d1d5db; /* gray-300 */
            cursor: pointer;
            border-radius: 50%;
            border: 2px solid #111827;
        }
        input[type="range"]::-moz-range-thumb {
            width: 20px;
            height: 20px;
            background: #d1d5db; /* gray-300 */
            cursor: pointer;
            border-radius: 50%;
        }
        .btn {
            background-color: #3b82f6;
            color: white;
            padding: 0.5rem 1.5rem;
            border-radius: 0.5rem;
            font-weight: 500;
            transition: background-color 0.2s;
        }
        .btn:hover {
            background-color: #2563eb;
        }
        .btn-secondary {
            background-color: #4b5563;
        }
        .btn-secondary:hover {
            background-color: #6b7280;
        }
        select {
            background-color: #1f2937;
            border: 1px solid #4b5563;
            color: #d1d5db;
            border-radius: 0.5rem;
            padding: 0.5rem;
        }
        .definition-title {
            color: #ffffff;
            font-weight: 600;
        }
    </style>
</head>
<body class="p-4 md:p-8">

    <div class="max-w-7xl mx-auto">
        <header class="text-center mb-8">
            <h1 class="text-4xl md:text-5xl font-bold text-gray-50">Series Convergence Visualizer</h1>
            <p class="text-lg text-gray-400 mt-2">Watch sums build to see convergence in action.</p>
        </header>

        <!-- Definitions Section -->
        <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
            <div class="info-card">
                <h3 class="text-xl definition-title">Converges</h3>
                <p class="mt-2 text-gray-400">The infinite sum approaches a <strong class="text-gray-200">single, finite number</strong>. On the graph, the line will flatten out and approach a horizontal asymptote.</p>
            </div>
            <div class="info-card">
                <h3 class="text-xl definition-title">Diverges</h3>
                <p class="mt-2 text-gray-400">The infinite sum does not settle on a single value. It might shoot off to <strong class="text-gray-200">infinity (∞ or -∞)</strong> or oscillate without approaching a limit.</p>
            </div>
        </div>


        <div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
            
            <!-- Control Panel -->
            <div class="lg:col-span-1 control-panel">
                <h2 class="text-2xl font-bold text-white border-b border-gray-700 pb-2">Controls</h2>
                
                <div class="mt-4">
                    <label for="series-type" class="block font-medium text-gray-300">Series Type</label>
                    <select id="series-type" class="w-full mt-1">
                        <option value="geometric">Geometric Series</option>
                        <option value="p-series">p-Series</option>
                        <option value="alternating-p">Alternating p-Series</option>
                        <option value="ratio-factorial">Ratio Test (Factorial)</option>
                        <option value="ratio-exponential">Ratio Test (Exponential)</option>
                    </select>
                </div>

                <!-- Parameter Sliders -->
                <div id="params-container" class="mt-6"></div>

                <div class="flex space-x-4 mt-6">
                    <button id="start-btn" class="btn flex-1">Start</button>
                    <button id="reset-btn" class="btn btn-secondary flex-1">Reset</button>
                </div>

                <!-- Info Display -->
                <div class="mt-8 p-4 bg-gray-950 rounded-lg border border-gray-700">
                    <h3 class="font-semibold text-lg text-white">Series Info</h3>
                    <div class="mt-2 text-sm space-y-2">
                        <p><strong>Formula:</strong> <span id="formula-display"></span></p>
                        <p><strong>Terms Added (n):</strong> <span id="n-display">0</span></p>
                        <p><strong>Partial Sum (S_n):</strong> <span id="sum-display">0.00</span></p>
                        <p><strong>Verdict:</strong> <span id="verdict-display" class="font-bold">Press Start</span></p>
                    </div>
                </div>
            </div>

            <!-- Chart -->
            <div class="lg:col-span-2 control-panel">
                <canvas id="seriesChart"></canvas>
            </div>
        </div>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function () {
            const seriesTypeSelect = document.getElementById('series-type');
            const paramsContainer = document.getElementById('params-container');
            const startBtn = document.getElementById('start-btn');
            const resetBtn = document.getElementById('reset-btn');

            const formulaDisplay = document.getElementById('formula-display');
            const nDisplay = document.getElementById('n-display');
            const sumDisplay = document.getElementById('sum-display');
            const verdictDisplay = document.getElementById('verdict-display');

            let chart;
            let animationFrameId;
            let state = {
                n: 0,
                sum: 0,
                isRunning: false,
                seriesType: 'geometric',
                params: {}
            };

            // Factorial function for calculations
            const factorials = [1];
            function factorial(n) {
                if (n < 0) return NaN;
                if (n >= factorials.length) {
                    for (let i = factorials.length; i <= n; i++) {
                        factorials[i] = factorials[i - 1] * i;
                    }
                }
                return factorials[n];
            }

            const seriesConfig = {
                'geometric': {
                    name: 'Geometric Series',
                    formula: (p) => `Σ a ⋅ rⁿ`,
                    params: {
                        a: { label: 'a (First Term)', min: -2, max: 2, step: 0.1, default: 1 },
                        r: { label: 'r (Ratio)', min: -1.5, max: 1.5, step: 0.01, default: 0.5 }
                    },
                    getTerm: (n, p) => p.a * Math.pow(p.r, n),
                    getVerdict: (p) => Math.abs(p.r) < 1 ? `Converges to ${ (p.a / (1 - p.r)).toFixed(4) }` : 'Diverges'
                },
                'p-series': {
                    name: 'p-Series',
                    formula: (p) => `Σ 1 / nᵖ`,
                    params: {
                        p: { label: 'p', min: 0, max: 3, step: 0.01, default: 2 }
                    },
                    getTerm: (n, p) => n > 0 ? 1 / Math.pow(n, p.p) : 0,
                    getVerdict: (p) => p.p > 1 ? 'Converges' : 'Diverges to ∞'
                },
                'alternating-p': {
                    name: 'Alternating p-Series',
                    formula: (p) => `Σ (-1)ⁿ⁻¹ / nᵖ`,
                    params: {
                        p: { label: 'p', min: 0, max: 3, step: 0.01, default: 1 }
                    },
                    getTerm: (n, p) => n > 0 ? Math.pow(-1, n - 1) / Math.pow(n, p.p) : 0,
                    getVerdict: (p) => p.p > 0 ? 'Converges' : 'Diverges'
                },
                'ratio-factorial': {
                    name: 'Ratio Test (Factorial)',
                    formula: (p) => `Σ xⁿ / n!`,
                    params: {
                        x: { label: 'x', min: -5, max: 5, step: 0.1, default: 1 }
                    },
                    getTerm: (n, p) => Math.pow(p.x, n) / factorial(n),
                    getVerdict: (p) => `Converges for all x (ROC = ∞). Sum is e^${p.x.toFixed(2)} ≈ ${Math.exp(p.x).toFixed(4)}`
                },
                'ratio-exponential': {
                    name: 'Ratio Test (Exponential)',
                    formula: (p) => `Σ n! ⋅ xⁿ`,
                    params: {
                        x: { label: 'x', min: -2, max: 2, step: 0.01, default: 0.5 }
                    },
                    getTerm: (n, p) => factorial(n) * Math.pow(p.x, n),
                    getVerdict: (p) => p.x === 0 ? 'Converges to 1 (at center x=0)' : 'Diverges for x ≠ 0 (ROC = 0)'
                }
            };

            function createParamControls() {
                const type = seriesTypeSelect.value;
                const config = seriesConfig[type];
                paramsContainer.innerHTML = '';
                state.params = {};

                for (const key in config.params) {
                    const param = config.params[key];
                    const container = document.createElement('div');
                    container.className = 'slider-container';
                    
                    const label = document.createElement('label');
                    label.className = 'block font-medium text-gray-300';
                    label.innerText = `${param.label}: `;
                    
                    const valueSpan = document.createElement('span');
                    valueSpan.id = `${key}-value`;
                    valueSpan.innerText = param.default;
                    label.appendChild(valueSpan);

                    const slider = document.createElement('input');
                    slider.type = 'range';
                    slider.id = `${key}-slider`;
                    slider.min = param.min;
                    slider.max = param.max;
                    slider.step = param.step;
                    slider.value = param.default;
                    
                    slider.addEventListener('input', (e) => {
                        state.params[key] = parseFloat(e.target.value);
                        valueSpan.innerText = parseFloat(e.target.value).toFixed(2);
                        if (!state.isRunning) {
                           resetSimulation();
                           updateDisplay();
                        }
                    });

                    container.appendChild(label);
                    container.appendChild(slider);
                    paramsContainer.appendChild(container);

                    state.params[key] = param.default;
                }
                state.seriesType = type;
                resetSimulation();
            }

            function initChart() {
                const ctx = document.getElementById('seriesChart').getContext('2d');
                if (chart) chart.destroy();
                chart = new Chart(ctx, {
                    type: 'line',
                    data: {
                        labels: [],
                        datasets: [{
                            label: 'Partial Sum (S_n)',
                            data: [],
                            borderColor: '#3b82f6',
                            backgroundColor: 'rgba(59, 130, 246, 0.1)',
                            borderWidth: 2,
                            pointRadius: 0,
                            tension: 0.1,
                            fill: true
                        }]
                    },
                    options: {
                        responsive: true,
                        maintainAspectRatio: false,
                        scales: {
                            x: {
                                title: { display: true, text: 'Number of Terms (n)', color: '#9ca3af' },
                                ticks: { color: '#9ca3af' },
                                grid: { color: 'rgba(255, 255, 255, 0.1)'}
                            },
                            y: {
                                title: { display: true, text: 'Sum', color: '#9ca3af' },
                                ticks: { color: '#9ca3af' },
                                grid: { color: 'rgba(255, 255, 255, 0.1)'}
                            }
                        },
                        plugins: {
                            legend: { labels: { color: '#9ca3af' } }
                        }
                    }
                });
            }

            function updateDisplay() {
                const config = seriesConfig[state.seriesType];
                formulaDisplay.innerHTML = config.formula(state.params)
                    .replace(/Σ/g, '<span class="text-2xl text-blue-400">Σ</span>')
                    .replace(/\*/g, '⋅')
                    .replace(/\^/g, '<sup>')
                    .replace(/p/g, 'ᵖ')
                    .replace(/n/g, 'ⁿ');
                nDisplay.textContent = state.n;
                sumDisplay.textContent = state.sum.toFixed(4);
                verdictDisplay.textContent = config.getVerdict(state.params);
            }

            function resetSimulation() {
                state.isRunning = false;
                startBtn.textContent = 'Start';
                cancelAnimationFrame(animationFrameId);
                
                state.n = 0;
                state.sum = seriesConfig[state.seriesType].getTerm(0, state.params);
                
                if (state.seriesType === 'p-series' || state.seriesType === 'alternating-p') {
                    state.n = 1;
                    state.sum = seriesConfig[state.seriesType].getTerm(1, state.params);
                }

                chart.data.labels = [state.n];
                chart.data.datasets[0].data = [state.sum];
                chart.update();
                updateDisplay();
            }

            function simulationLoop() {
                if (!state.isRunning) return;

                const config = seriesConfig[state.seriesType];
                
                for (let i = 0; i < 5; i++) { 
                    if (state.n > 1000) { // Safety break
                        state.isRunning = false;
                        startBtn.textContent = 'Start';
                        break;
                    }
                    state.n++;
                    const term = config.getTerm(state.n, state.params);
                    if (isFinite(term)) {
                        state.sum += term;
                    } else {
                        state.isRunning = false;
                        startBtn.textContent = 'Start';
                        break;
                    }

                    if (state.n % 5 === 0) {
                        chart.data.labels.push(state.n);
                        chart.data.datasets[0].data.push(state.sum);
                    }
                }
                
                chart.update('none');
                updateDisplay();

                if (Math.abs(state.sum) > 1e6) {
                    verdictDisplay.textContent = 'Diverging to ∞';
                    state.isRunning = false;
                    startBtn.textContent = 'Start';
                    return;
                }

                animationFrameId = requestAnimationFrame(simulationLoop);
            }

            startBtn.addEventListener('click', () => {
                state.isRunning = !state.isRunning;
                if (state.isRunning) {
                    startBtn.textContent = 'Pause';
                    simulationLoop();
                } else {
                    startBtn.textContent = 'Start';
                    cancelAnimationFrame(animationFrameId);
                }
            });

            resetBtn.addEventListener('click', () => {
                createParamControls();
            });

            seriesTypeSelect.addEventListener('change', () => {
                createParamControls();
            });

            // Initial setup
            initChart();
            createParamControls();
        });
    </script>
</body>
</html>