Changes to filtervis.scroll.pub

root
root
25 days ago
Initial commit
body.html
Changed around line 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+

Digital Filter Visualization

+

Explore and understand FIR and IIR filters through interactive visualizations

+
+
+
+

FIR Filter Visualization

+
+
+
+
+ 10
+
+
+
+
+ 0.5
+
+
+
+
+
+
+
+
+

IIR Filter Visualization

+
+
+
+
+ 4
+
+
+
+
+ 0.5
+
+
+
+
+
+
+
+
+

About Digital Filters

+
+
+

FIR Filters

+

Finite Impulse Response filters are digital filters with a finite impulse response duration.

+
+
+

IIR Filters

+

Infinite Impulse Response filters are digital filters with an infinite impulse response duration.

+
+
+
+
+
+
+

Created with passion by FilterVis

+
index.scroll
Changed around line 1
+ buildHtml
+ baseUrl https://filtervis.scroll.pub
+ metaTags
+ editButton /edit.html
+ title Digital Filter Visualization Tool
+ style.css
+ body.html
+ script.js
prompt.json
Changed around line 1
+ {
+ "id": "msg_01AbxBESfUi5rQ6STMYciVUX",
+ "type": "message",
+ "role": "assistant",
+ "model": "claude-3-5-sonnet-20241022",
+ "content": [
+ {
+ "type": "text",
+ "text": "I'll create a website for visualizing FIR and IIR digital filters with interactive elements and beautiful visualizations.\n\n---domain---\nfiltervis.scroll.pub\n\n---index.scroll---\nbuildHtml\nbaseUrl https://filtervis.scroll.pub\nmetaTags\neditButton /edit.html\ntitle Digital Filter Visualization Tool\nstyle.css\nbody.html\nscript.js\n\n---body.html---\n
\n \n
\n\n
\n
\n

Digital Filter Visualization

\n

Explore and understand FIR and IIR filters through interactive visualizations

\n
\n\n
\n

FIR Filter Visualization

\n
\n
\n \n \n 10\n
\n
\n \n \n 0.5\n
\n
\n
\n \n
\n
\n\n
\n

IIR Filter Visualization

\n
\n
\n \n \n 4\n
\n
\n \n \n 0.5\n
\n
\n
\n \n
\n
\n\n
\n

About Digital Filters

\n
\n
\n

FIR Filters

\n

Finite Impulse Response filters are digital filters with a finite impulse response duration.

\n
\n
\n

IIR Filters

\n

Infinite Impulse Response filters are digital filters with an infinite impulse response duration.

\n
\n
\n
\n
\n\n
\n

Created with passion by FilterVis

\n
\n\n---style.css---\n:root {\n --primary: #2d3436;\n --secondary: #0984e3;\n --accent: #00cec9;\n --background: #f5f6fa;\n --text: #2d3436;\n}\n\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n line-height: 1.6;\n color: var(--text);\n background: var(--background);\n}\n\nheader {\n background: var(--primary);\n padding: 1rem;\n position: fixed;\n width: 100%;\n top: 0;\n z-index: 1000;\n box-shadow: 0 2px 10px rgba(0,0,0,0.1);\n}\n\nnav {\n max-width: 1200px;\n margin: 0 auto;\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.logo {\n color: white;\n font-size: 1.5rem;\n font-weight: bold;\n text-decoration: none;\n background: linear-gradient(45deg, var(--secondary), var(--accent));\n -webkit-background-clip: text;\n -webkit-text-fill-color: transparent;\n}\n\n.nav-links {\n display: flex;\n list-style: none;\n gap: 2rem;\n}\n\n.nav-links a {\n color: white;\n text-decoration: none;\n transition: color 0.3s ease;\n}\n\n.nav-links a:hover {\n color: var(--accent);\n}\n\n.menu-toggle {\n display: none;\n}\n\nmain {\n margin-top: 4rem;\n padding: 2rem;\n}\n\n.hero {\n text-align: center;\n padding: 4rem 2rem;\n background: linear-gradient(135deg, var(--primary), var(--secondary));\n color: white;\n border-radius: 1rem;\n margin-bottom: 3rem;\n}\n\n.hero h1 {\n font-size: 3rem;\n margin-bottom: 1rem;\n}\n\n.filter-section {\n background: white;\n padding: 2rem;\n border-radius: 1rem;\n margin-bottom: 3rem;\n box-shadow: 0 4px 6px rgba(0,0,0,0.1);\n}\n\n.filter-controls {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));\n gap: 2rem;\n margin-bottom: 2rem;\n}\n\n.parameter-group {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\ninput[type=\"range\"] {\n width: 100%;\n height: 6px;\n -webkit-appearance: none;\n background: var(--background);\n border-radius: 3px;\n outline: none;\n}\n\ninput[type=\"range\"]::-webkit-slider-thumb {\n -webkit-appearance: none;\n width: 18px;\n height: 18px;\n background: var(--secondary);\n border-radius: 50%;\n cursor: pointer;\n transition: background 0.3s ease;\n}\n\ninput[type=\"range\"]::-webkit-slider-thumb:hover {\n background: var(--accent);\n}\n\n.visualization {\n background: var(--background);\n border-radius: 0.5rem;\n padding: 1rem;\n overflow: hidden;\n}\n\ncanvas {\n width: 100%;\n height: auto;\n display: block;\n}\n\n.info-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));\n gap: 2rem;\n margin-top: 2rem;\n}\n\n.info-card {\n background: white;\n padding: 2rem;\n border-radius: 1rem;\n box-shadow: 0 4px 6px rgba(0,0,0,0.1);\n transition: transform 0.3s ease;\n}\n\n.info-card:hover {\n transform: translateY(-5px);\n}\n\nfooter {\n background: var(--primary);\n color: white;\n text-align: center;\n padding: 2rem;\n margin-top: 4rem;\n}\n\n@media (max-width: 768px) {\n .menu-toggle {\n display: block;\n background: none;\n border: none;\n cursor: pointer;\n padding: 0.5rem;\n }\n\n .menu-toggle span {\n display: block;\n width: 25px;\n height: 3px;\n background: white;\n margin: 5px 0;\n transition: 0.3s;\n }\n\n .nav-links {\n display: none;\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n background: var(--primary);\n flex-direction: column;\n padding: 1rem;\n text-align: center;\n }\n\n .nav-links.active {\n display: flex;\n }\n\n .hero h1 {\n font-size: 2rem;\n }\n}\n\n---script.js---\ndocument.addEventListener('DOMContentLoaded', () => {\n // Mobile menu toggle\n const menuToggle = document.querySelector('.menu-toggle');\n const navLinks = document.querySelector('.nav-links');\n \n menuToggle.addEventListener('click', () => {\n navLinks.classList.toggle('active');\n });\n\n // Filter parameters\n const firOrder = document.getElementById('firOrder');\n const firCutoff = document.getElementById('firCutoff');\n const iirOrder = document.getElementById('iirOrder');\n const iirCutoff = document.getElementById('iirCutoff');\n\n // Update value displays\n const updateValue = (input) => {\n const display = input.parentElement.querySelector('.value-display');\n display.textContent = input.value;\n };\n\n [firOrder, firCutoff, iirOrder, iirCutoff].forEach(input => {\n input.addEventListener('input', () => updateValue(input));\n updateValue(input);\n });\n\n // Canvas setup\n const firCanvas = document.getElementById('firCanvas');\n const iirCanvas = document.getElementById('iirCanvas');\n const firCtx = firCanvas.getContext('2d');\n const iirCtx = iirCanvas.getContext('2d');\n\n // Drawing functions\n const drawFrequencyResponse = (ctx, canvas, order, cutoff, type) => {\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n \n // Set up coordinate system\n ctx.beginPath();\n ctx.strokeStyle = '#dfe6e9';\n ctx.moveTo(0, canvas.height/2);\n ctx.lineTo(canvas.width, canvas.height/2);\n ctx.moveTo(0, 0);\n ctx.lineTo(0, canvas.height);\n ctx.stroke();\n\n // Draw frequency response\n ctx.beginPath();\n ctx.strokeStyle = type === 'FIR' ? '#0984e3' : '#00cec9';\n ctx.lineWidth = 2;\n\n for(let x = 0; x < canvas.width; x++) {\n const freq = x / canvas.width;\n let response;\n \n if(type === 'FIR') {\n response = Math.sin(Math.PI * freq * order) / (Math.PI * freq * order);\n response = Math.abs(response);\n } else {\n response = 1 / (1 + Math.pow(freq/cutoff, 2*order));\n }\n\n const y = canvas.height - (response * canvas.height * 0.8);\n \n if(x === 0) {\n ctx.moveTo(x, y);\n } else {\n ctx.lineTo(x, y);\n }\n }\n \n ctx.stroke();\n };\n\n // Initial draws\n drawFrequencyResponse(firCtx, firCanvas, firOrder.value, firCutoff.value, 'FIR');\n drawFrequencyResponse(iirCtx, iirCanvas, iirOrder.value, iirCutoff.value, 'IIR');\n\n // Update on parameter change\n [firOrder, firCutoff].forEach(input => {\n input.addEventListener('input', () => {\n drawFrequencyResponse(firCtx, firCanvas, firOrder.value, firCutoff.value, 'FIR');\n });\n });\n\n [iirOrder, iirCutoff].forEach(input => {\n input.addEventListener('input', () => {\n drawFrequencyResponse(iirCtx, iirCanvas, iirOrder.value, iirCutoff.value, 'IIR');\n });\n });\n\n // Resize handling\n window.addEventListener('resize', () => {\n drawFrequencyResponse(firCtx, firCanvas, firOrder.value, firCutoff.value, 'FIR');\n drawFrequencyResponse(iirCtx, iirCanvas, iirOrder.value, iirCutoff.value, 'IIR');\n });\n});\n\n---end---\n\nThis creates a beautiful, interactive website for visualizing FIR and IIR filters. Key features:\n\n1. Interactive filter parameter controls\n2. Real-time visualization of frequency responses\n3. Responsive design that works on all devices\n4. Beautiful gradient-based color scheme\n5. Smooth animations and transitions\n6. Accessible controls and semantic HTML\n7. Clean, modern interface\n8. Educational information about filters\n9. Mobile-friendly navigation\n10. Canvas-based visualizations\n\nThe domain name \"filtervis\" is short, memorable, and clearly indicates the purpose of the website."
+ }
+ ],
+ "stop_reason": "end_turn",
+ "stop_sequence": null,
+ "usage": {
+ "input_tokens": 285,
+ "cache_creation_input_tokens": 0,
+ "cache_read_input_tokens": 0,
+ "output_tokens": 3521
+ }
+ }
readme.scroll
Changed around line 1
+ # filtervis.scroll.pub
+ Website generated by Claude from prompt: I want a website to visualise FIR and IIR filters.
script.js
Changed around line 1
+ document.addEventListener('DOMContentLoaded', () => {
+ // Mobile menu toggle
+ const menuToggle = document.querySelector('.menu-toggle');
+ const navLinks = document.querySelector('.nav-links');
+
+ menuToggle.addEventListener('click', () => {
+ navLinks.classList.toggle('active');
+ });
+
+ // Filter parameters
+ const firOrder = document.getElementById('firOrder');
+ const firCutoff = document.getElementById('firCutoff');
+ const iirOrder = document.getElementById('iirOrder');
+ const iirCutoff = document.getElementById('iirCutoff');
+
+ // Update value displays
+ const updateValue = (input) => {
+ const display = input.parentElement.querySelector('.value-display');
+ display.textContent = input.value;
+ };
+
+ [firOrder, firCutoff, iirOrder, iirCutoff].forEach(input => {
+ input.addEventListener('input', () => updateValue(input));
+ updateValue(input);
+ });
+
+ // Canvas setup
+ const firCanvas = document.getElementById('firCanvas');
+ const iirCanvas = document.getElementById('iirCanvas');
+ const firCtx = firCanvas.getContext('2d');
+ const iirCtx = iirCanvas.getContext('2d');
+
+ // Drawing functions
+ const drawFrequencyResponse = (ctx, canvas, order, cutoff, type) => {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ // Set up coordinate system
+ ctx.beginPath();
+ ctx.strokeStyle = '#dfe6e9';
+ ctx.moveTo(0, canvas.height/2);
+ ctx.lineTo(canvas.width, canvas.height/2);
+ ctx.moveTo(0, 0);
+ ctx.lineTo(0, canvas.height);
+ ctx.stroke();
+
+ // Draw frequency response
+ ctx.beginPath();
+ ctx.strokeStyle = type === 'FIR' ? '#0984e3' : '#00cec9';
+ ctx.lineWidth = 2;
+
+ for(let x = 0; x < canvas.width; x++) {
+ const freq = x / canvas.width;
+ let response;
+
+ if(type === 'FIR') {
+ response = Math.sin(Math.PI * freq * order) / (Math.PI * freq * order);
+ response = Math.abs(response);
+ } else {
+ response = 1 / (1 + Math.pow(freq/cutoff, 2*order));
+ }
+
+ const y = canvas.height - (response * canvas.height * 0.8);
+
+ if(x === 0) {
+ ctx.moveTo(x, y);
+ } else {
+ ctx.lineTo(x, y);
+ }
+ }
+
+ ctx.stroke();
+ };
+
+ // Initial draws
+ drawFrequencyResponse(firCtx, firCanvas, firOrder.value, firCutoff.value, 'FIR');
+ drawFrequencyResponse(iirCtx, iirCanvas, iirOrder.value, iirCutoff.value, 'IIR');
+
+ // Update on parameter change
+ [firOrder, firCutoff].forEach(input => {
+ input.addEventListener('input', () => {
+ drawFrequencyResponse(firCtx, firCanvas, firOrder.value, firCutoff.value, 'FIR');
+ });
+ });
+
+ [iirOrder, iirCutoff].forEach(input => {
+ input.addEventListener('input', () => {
+ drawFrequencyResponse(iirCtx, iirCanvas, iirOrder.value, iirCutoff.value, 'IIR');
+ });
+ });
+
+ // Resize handling
+ window.addEventListener('resize', () => {
+ drawFrequencyResponse(firCtx, firCanvas, firOrder.value, firCutoff.value, 'FIR');
+ drawFrequencyResponse(iirCtx, iirCanvas, iirOrder.value, iirCutoff.value, 'IIR');
+ });
+ });
style.css
Changed around line 1
+ :root {
+ --primary: #2d3436;
+ --secondary: #0984e3;
+ --accent: #00cec9;
+ --background: #f5f6fa;
+ --text: #2d3436;
+ }
+
+ * {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ }
+
+ body {
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+ line-height: 1.6;
+ color: var(--text);
+ background: var(--background);
+ }
+
+ header {
+ background: var(--primary);
+ padding: 1rem;
+ position: fixed;
+ width: 100%;
+ top: 0;
+ z-index: 1000;
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
+ }
+
+ nav {
+ max-width: 1200px;
+ margin: 0 auto;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ .logo {
+ color: white;
+ font-size: 1.5rem;
+ font-weight: bold;
+ text-decoration: none;
+ background: linear-gradient(45deg, var(--secondary), var(--accent));
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ }
+
+ .nav-links {
+ display: flex;
+ list-style: none;
+ gap: 2rem;
+ }
+
+ .nav-links a {
+ color: white;
+ text-decoration: none;
+ transition: color 0.3s ease;
+ }
+
+ .nav-links a:hover {
+ color: var(--accent);
+ }
+
+ .menu-toggle {
+ display: none;
+ }
+
+ main {
+ margin-top: 4rem;
+ padding: 2rem;
+ }
+
+ .hero {
+ text-align: center;
+ padding: 4rem 2rem;
+ background: linear-gradient(135deg, var(--primary), var(--secondary));
+ color: white;
+ border-radius: 1rem;
+ margin-bottom: 3rem;
+ }
+
+ .hero h1 {
+ font-size: 3rem;
+ margin-bottom: 1rem;
+ }
+
+ .filter-section {
+ background: white;
+ padding: 2rem;
+ border-radius: 1rem;
+ margin-bottom: 3rem;
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
+ }
+
+ .filter-controls {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+ gap: 2rem;
+ margin-bottom: 2rem;
+ }
+
+ .parameter-group {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+ }
+
+ input[type="range"] {
+ width: 100%;
+ height: 6px;
+ -webkit-appearance: none;
+ background: var(--background);
+ border-radius: 3px;
+ outline: none;
+ }
+
+ input[type="range"]::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ width: 18px;
+ height: 18px;
+ background: var(--secondary);
+ border-radius: 50%;
+ cursor: pointer;
+ transition: background 0.3s ease;
+ }
+
+ input[type="range"]::-webkit-slider-thumb:hover {
+ background: var(--accent);
+ }
+
+ .visualization {
+ background: var(--background);
+ border-radius: 0.5rem;
+ padding: 1rem;
+ overflow: hidden;
+ }
+
+ canvas {
+ width: 100%;
+ height: auto;
+ display: block;
+ }
+
+ .info-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+ gap: 2rem;
+ margin-top: 2rem;
+ }
+
+ .info-card {
+ background: white;
+ padding: 2rem;
+ border-radius: 1rem;
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
+ transition: transform 0.3s ease;
+ }
+
+ .info-card:hover {
+ transform: translateY(-5px);
+ }
+
+ footer {
+ background: var(--primary);
+ color: white;
+ text-align: center;
+ padding: 2rem;
+ margin-top: 4rem;
+ }
+
+ @media (max-width: 768px) {
+ .menu-toggle {
+ display: block;
+ background: none;
+ border: none;
+ cursor: pointer;
+ padding: 0.5rem;
+ }
+
+ .menu-toggle span {
+ display: block;
+ width: 25px;
+ height: 3px;
+ background: white;
+ margin: 5px 0;
+ transition: 0.3s;
+ }
+
+ .nav-links {
+ display: none;
+ position: absolute;
+ top: 100%;
+ left: 0;
+ right: 0;
+ background: var(--primary);
+ flex-direction: column;
+ padding: 1rem;
+ text-align: center;
+ }
+
+ .nav-links.active {
+ display: flex;
+ }
+
+ .hero h1 {
+ font-size: 2rem;
+ }
+ }