commit e7f3a6c76e1b1b8a268df2cf2f59977acbbce87d
Author: root <root@hub.scroll.pub>
Date: 2024-12-27 18:58:47 +0000
Subject: Initial commit
diff --git a/.log.txt b/.log.txt
new file mode 100644
index 0000000..d2be333
--- /dev/null
+++ b/.log.txt
@@ -0,0 +1,3 @@
+read signalgen.scroll.pub https://hub.scroll.pub/edit.html?folderName=signalgen.scroll.pub 1735325919854 ::ffff:37.19.196.51 0.0 200 Chrome.131 MacOSX.10.15.7
+read signalgen.scroll.pub https://hub.scroll.pub/ls.json?folderName=signalgen.scroll.pub 1735325924116 ::ffff:37.19.196.51 0.0 200 Chrome.131 MacOSX.10.15.7
+read signalgen.scroll.pub https://hub.scroll.pub/readFile.htm?folderName=signalgen.scroll.pub&filePath=signalgen.scroll.pub%2Findex.scroll 1735325924313 ::ffff:37.19.196.51 0.0 200 Chrome.131 MacOSX.10.15.7
diff --git a/.stats.json b/.stats.json
new file mode 100644
index 0000000..ee4bcc9
--- /dev/null
+++ b/.stats.json
@@ -0,0 +1,21 @@
+{
+ "files": [
+ "body.html",
+ "index.scroll",
+ "prompt.json",
+ "readme.scroll",
+ "script.js",
+ "style.css"
+ ],
+ "hasSslCert": false,
+ "stats": {
+ "folder": "signalgen.scroll.pub",
+ "folderLink": "http://signalgen.scroll.pub",
+ "created": "2024-12-27T18:58:38.000Z",
+ "revised": "2024-12-27T18:58:38.000Z",
+ "files": 6,
+ "mb": 1,
+ "revisions": 1,
+ "hash": "5ef5be4729"
+ }
+}
\ No newline at end of file
diff --git a/body.html b/body.html
index 6155a81..1dbb9a5 100644
--- a/body.html
+++ b/body.html
@@ -1,48 +1,77 @@
<header>
- <h1>Signal Generator</h1>
- <p>Generate periodic signals f1 and f2, view their spectral representation, and perform basic operations.</p>
+ <h1>Signal Generator & Spectrum Analyzer</h1>
</header>
<main>
- <section class="controls">
- <div class="input-group">
- <label for="f1-frequency">Frequency f1 (Hz):</label>
- <input type="number" id="f1-frequency" value="1" min="0.1" step="0.1">
- </div>
- <div class="input-group">
- <label for="f2-frequency">Frequency f2 (Hz):</label>
- <input type="number" id="f2-frequency" value="2" min="0.1" step="0.1">
- </div>
- <div class="input-group">
- <label for="amplitude">Amplitude:</label>
- <input type="number" id="amplitude" value="1" min="0.1" step="0.1">
- </div>
- <div class="input-group">
- <label for="phase">Phase (rad):</label>
- <input type="number" id="phase" value="0" min="0" step="0.1">
- </div>
- <button id="generate-btn">Generate Signals</button>
- </section>
+ <div class="controls-container">
+ <section class="signal-controls">
+ <h2>Signal 1 (f1)</h2>
+ <div class="control-group">
+ <label for="f1-type">Waveform:</label>
+ <select id="f1-type">
+ <option value="sine">Sine</option>
+ <option value="square">Square</option>
+ <option value="triangle">Triangle</option>
+ <option value="sawtooth">Sawtooth</option>
+ </select>
+ </div>
+ <div class="control-group">
+ <label for="f1-frequency">Frequency (Hz):</label>
+ <input type="range" id="f1-frequency" min="1" max="100" value="10">
+ <span class="value-display" id="f1-freq-value">10 Hz</span>
+ </div>
+ <div class="control-group">
+ <label for="f1-amplitude">Amplitude:</label>
+ <input type="range" id="f1-amplitude" min="0" max="1" step="0.1" value="1">
+ <span class="value-display" id="f1-amp-value">1.0</span>
+ </div>
+ </section>
- <section class="visualization">
- <div class="chart-container">
+ <section class="signal-controls">
+ <h2>Signal 2 (f2)</h2>
+ <div class="control-group">
+ <label for="f2-type">Waveform:</label>
+ <select id="f2-type">
+ <option value="sine">Sine</option>
+ <option value="square">Square</option>
+ <option value="triangle">Triangle</option>
+ <option value="sawtooth">Sawtooth</option>
+ </select>
+ </div>
+ <div class="control-group">
+ <label for="f2-frequency">Frequency (Hz):</label>
+ <input type="range" id="f2-frequency" min="1" max="100" value="20">
+ <span class="value-display" id="f2-freq-value">20 Hz</span>
+ </div>
+ <div class="control-group">
+ <label for="f2-amplitude">Amplitude:</label>
+ <input type="range" id="f2-amplitude" min="0" max="1" step="0.1" value="1">
+ <span class="value-display" id="f2-amp-value">1.0</span>
+ </div>
+ </section>
+
+ <section class="operations">
+ <h2>Operations</h2>
+ <div class="button-group">
+ <button id="btn-add">Add (f1 + f2)</button>
+ <button id="btn-multiply">Multiply (f1 × f2)</button>
+ <button id="btn-reset">Reset</button>
+ </div>
+ </section>
+ </div>
+
+ <div class="canvas-container">
+ <section class="visualization">
+ <h3>Time Domain</h3>
<canvas id="time-domain"></canvas>
- </div>
- <div class="chart-container">
+ </section>
+ <section class="visualization">
+ <h3>Frequency Domain</h3>
<canvas id="frequency-domain"></canvas>
- </div>
- </section>
-
- <section class="operations">
- <h2>Signal Operations</h2>
- <div class="operation-buttons">
- <button id="add-btn">Add Signals</button>
- <button id="multiply-btn">Multiply Signals</button>
- <button id="subtract-btn">Subtract Signals</button>
- </div>
- </section>
+ </section>
+ </div>
</main>
<footer>
- <p>Explore the fascinating world of signal processing with this interactive tool.</p>
-</footer>
\ No newline at end of file
+ <p>Created with passion by SignalGen</p>
+</footer>
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..9aa5d61
--- /dev/null
+++ b/index.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>Signal Generator - Periodic Signals and Spectral Representation</title>
+ <script>/* This HTML was generated by 📜 Scroll v164.7.0. https://scroll.pub */</script>
+ <style>@media print {.doNotPrint {display: none !important;}}</style>
+ <link rel="canonical" href="https://signalgen.scroll.pub/index.html">
+ <meta name="viewport" content="width=device-width,initial-scale=1">
+ <meta name="description" content="">
+ <meta name="generator" content="Scroll v164.7.0">
+ <meta property="og:title" content="Signal Generator - Periodic Signals and Spectral Representation">
+ <meta property="og:description" content="">
+ <meta property="og:image" content="">
+
+ <link rel="source" type="application/git" title="Source Code Repository" href="index.scroll">
+
+ <meta name="twitter:card" content="summary_large_image">
+</head>
+<body>
+<style>.abstractIconButtonParser {position:absolute;top:0.25rem; }.abstractIconButtonParser svg {fill: rgba(204,204,204,.8);width:1.875rem;height:1.875rem; padding: 0 7px;} .abstractIconButtonParser:hover svg{fill: #333;}</style><a href="/edit.html" class="doNotPrint abstractIconButtonParser" style="right:2rem;"><svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M21.1213 2.70705C19.9497 1.53548 18.0503 1.53547 16.8787 2.70705L15.1989 4.38685L7.29289 12.2928C7.16473 12.421 7.07382 12.5816 7.02986 12.7574L6.02986 16.7574C5.94466 17.0982 6.04451 17.4587 6.29289 17.707C6.54127 17.9554 6.90176 18.0553 7.24254 17.9701L11.2425 16.9701C11.4184 16.9261 11.5789 16.8352 11.7071 16.707L19.5556 8.85857L21.2929 7.12126C22.4645 5.94969 22.4645 4.05019 21.2929 2.87862L21.1213 2.70705ZM18.2929 4.12126C18.6834 3.73074 19.3166 3.73074 19.7071 4.12126L19.8787 4.29283C20.2692 4.68336 20.2692 5.31653 19.8787 5.70705L18.8622 6.72357L17.3068 5.10738L18.2929 4.12126ZM15.8923 6.52185L17.4477 8.13804L10.4888 15.097L8.37437 15.6256L8.90296 13.5112L15.8923 6.52185ZM4 7.99994C4 7.44766 4.44772 6.99994 5 6.99994H10C10.5523 6.99994 11 6.55223 11 5.99994C11 5.44766 10.5523 4.99994 10 4.99994H5C3.34315 4.99994 2 6.34309 2 7.99994V18.9999C2 20.6568 3.34315 21.9999 5 21.9999H16C17.6569 21.9999 19 20.6568 19 18.9999V13.9999C19 13.4477 18.5523 12.9999 18 12.9999C17.4477 12.9999 17 13.4477 17 13.9999V18.9999C17 19.5522 16.5523 19.9999 16 19.9999H5C4.44772 19.9999 4 19.5522 4 18.9999V7.99994Z"/></svg></a>
+<link rel="stylesheet" type="text/css" href="style.css">
+<header>
+ <h1>Signal Generator</h1>
+ <p>Generate periodic signals f1 and f2, view their spectral representation, and perform basic operations.</p>
+</header>
+
+<main>
+ <section class="controls">
+ <div class="input-group">
+ <label for="f1-frequency">Frequency f1 (Hz):</label>
+ <input type="number" id="f1-frequency" value="1" min="0.1" step="0.1">
+ </div>
+ <div class="input-group">
+ <label for="f2-frequency">Frequency f2 (Hz):</label>
+ <input type="number" id="f2-frequency" value="2" min="0.1" step="0.1">
+ </div>
+ <div class="input-group">
+ <label for="amplitude">Amplitude:</label>
+ <input type="number" id="amplitude" value="1" min="0.1" step="0.1">
+ </div>
+ <div class="input-group">
+ <label for="phase">Phase (rad):</label>
+ <input type="number" id="phase" value="0" min="0" step="0.1">
+ </div>
+ <button id="generate-btn">Generate Signals</button>
+ </section>
+
+ <section class="visualization">
+ <div class="chart-container">
+ <canvas id="time-domain"></canvas>
+ </div>
+ <div class="chart-container">
+ <canvas id="frequency-domain"></canvas>
+ </div>
+ </section>
+
+ <section class="operations">
+ <h2>Signal Operations</h2>
+ <div class="operation-buttons">
+ <button id="add-btn">Add Signals</button>
+ <button id="multiply-btn">Multiply Signals</button>
+ <button id="subtract-btn">Subtract Signals</button>
+ </div>
+ </section>
+</main>
+
+<footer>
+ <p>Explore the fascinating world of signal processing with this interactive tool.</p>
+</footer>
+<script src="script.js"></script>
+</body>
+</html>
\ No newline at end of file
diff --git a/index.scroll b/index.scroll
index c5cf75b..bbf618f 100644
--- a/index.scroll
+++ b/index.scroll
@@ -2,7 +2,7 @@ buildHtml
baseUrl https://signalgen.scroll.pub
metaTags
editButton /edit.html
-title Signal Generator - Periodic Signals and Spectral Representation
+title Signal Generator & Spectrum Analyzer
style.css
body.html
-script.js
\ No newline at end of file
+script.js
diff --git a/prompt.json b/prompt.json
index dfcb8f0..72b109f 100644
--- a/prompt.json
+++ b/prompt.json
@@ -1,25 +1,20 @@
{
- "id": "00922dd1-9cc4-499c-9182-bd47d8730d55",
- "object": "chat.completion",
- "created": 1735325886,
- "model": "deepseek-chat",
- "choices": [
+ "id": "msg_01FhnbhUyGN4yaG6NGSYPkvx",
+ "type": "message",
+ "role": "assistant",
+ "model": "claude-3-5-sonnet-20241022",
+ "content": [
{
- "index": 0,
- "message": {
- "role": "assistant",
- "content": "---domain---\nsignalgen.scroll.pub\n---index.scroll---\nbuildHtml\nbaseUrl https://signalgen.scroll.pub\nmetaTags\neditButton /edit.html\ntitle Signal Generator - Periodic Signals and Spectral Representation\nstyle.css\nbody.html\nscript.js\n---body.html---\n<header>\n <h1>Signal Generator</h1>\n <p>Generate periodic signals f1 and f2, view their spectral representation, and perform basic operations.</p>\n</header>\n\n<main>\n <section class=\"controls\">\n <div class=\"input-group\">\n <label for=\"f1-frequency\">Frequency f1 (Hz):</label>\n <input type=\"number\" id=\"f1-frequency\" value=\"1\" min=\"0.1\" step=\"0.1\">\n </div>\n <div class=\"input-group\">\n <label for=\"f2-frequency\">Frequency f2 (Hz):</label>\n <input type=\"number\" id=\"f2-frequency\" value=\"2\" min=\"0.1\" step=\"0.1\">\n </div>\n <div class=\"input-group\">\n <label for=\"amplitude\">Amplitude:</label>\n <input type=\"number\" id=\"amplitude\" value=\"1\" min=\"0.1\" step=\"0.1\">\n </div>\n <div class=\"input-group\">\n <label for=\"phase\">Phase (rad):</label>\n <input type=\"number\" id=\"phase\" value=\"0\" min=\"0\" step=\"0.1\">\n </div>\n <button id=\"generate-btn\">Generate Signals</button>\n </section>\n\n <section class=\"visualization\">\n <div class=\"chart-container\">\n <canvas id=\"time-domain\"></canvas>\n </div>\n <div class=\"chart-container\">\n <canvas id=\"frequency-domain\"></canvas>\n </div>\n </section>\n\n <section class=\"operations\">\n <h2>Signal Operations</h2>\n <div class=\"operation-buttons\">\n <button id=\"add-btn\">Add Signals</button>\n <button id=\"multiply-btn\">Multiply Signals</button>\n <button id=\"subtract-btn\">Subtract Signals</button>\n </div>\n </section>\n</main>\n\n<footer>\n <p>Explore the fascinating world of signal processing with this interactive tool.</p>\n</footer>\n---style.css---\n:root {\n --primary-color: #4a90e2;\n --secondary-color: #50e3c2;\n --background-color: #f5f7fa;\n --text-color: #333;\n --border-radius: 8px;\n --box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\n}\n\nbody {\n font-family: 'Segoe UI', sans-serif;\n line-height: 1.6;\n margin: 0;\n padding: 0;\n background-color: var(--background-color);\n color: var(--text-color);\n}\n\nheader {\n background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));\n color: white;\n padding: 2rem;\n text-align: center;\n border-radius: 0 0 var(--border-radius) var(--border-radius);\n}\n\nmain {\n max-width: 1200px;\n margin: 2rem auto;\n padding: 0 1rem;\n}\n\n.controls {\n background: white;\n padding: 1.5rem;\n border-radius: var(--border-radius);\n box-shadow: var(--box-shadow);\n margin-bottom: 2rem;\n}\n\n.input-group {\n margin-bottom: 1rem;\n}\n\nlabel {\n display: block;\n margin-bottom: 0.5rem;\n font-weight: 500;\n}\n\ninput {\n width: 100%;\n padding: 0.5rem;\n border: 1px solid #ddd;\n border-radius: var(--border-radius);\n}\n\nbutton {\n background-color: var(--primary-color);\n color: white;\n border: none;\n padding: 0.75rem 1.5rem;\n border-radius: var(--border-radius);\n cursor: pointer;\n transition: background-color 0.3s ease;\n}\n\nbutton:hover {\n background-color: darken(var(--primary-color), 10%);\n}\n\n.visualization {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));\n gap: 2rem;\n margin-bottom: 2rem;\n}\n\n.chart-container {\n background: white;\n padding: 1.5rem;\n border-radius: var(--border-radius);\n box-shadow: var(--box-shadow);\n}\n\n.operations {\n text-align: center;\n margin-bottom: 2rem;\n}\n\n.operation-buttons {\n display: flex;\n justify-content: center;\n gap: 1rem;\n flex-wrap: wrap;\n}\n\nfooter {\n text-align: center;\n padding: 1rem;\n background: white;\n margin-top: 2rem;\n border-radius: var(--border-radius) var(--border-radius) 0 0;\n box-shadow: var(--box-shadow);\n}\n\n@media (max-width: 768px) {\n .visualization {\n grid-template-columns: 1fr;\n }\n}\n---script.js---\nconst f1FrequencyInput = document.getElementById('f1-frequency');\nconst f2FrequencyInput = document.getElementById('f2-frequency');\nconst amplitudeInput = document.getElementById('amplitude');\nconst phaseInput = document.getElementById('phase');\nconst generateBtn = document.getElementById('generate-btn');\nconst addBtn = document.getElementById('add-btn');\nconst multiplyBtn = document.getElementById('multiply-btn');\nconst subtractBtn = document.getElementById('subtract-btn');\nconst timeDomainCanvas = document.getElementById('time-domain');\nconst frequencyDomainCanvas = document.getElementById('frequency-domain');\n\nlet f1Signal = [];\nlet f2Signal = [];\nlet combinedSignal = [];\n\nfunction generateSineWave(frequency, amplitude, phase, sampleRate = 44100, duration = 1) {\n const signal = [];\n const numSamples = sampleRate * duration;\n for (let i = 0; i < numSamples; i++) {\n const t = i / sampleRate;\n signal.push(amplitude * Math.sin(2 * Math.PI * frequency * t + phase));\n }\n return signal;\n}\n\nfunction plotTimeDomain(signal, canvas) {\n const ctx = canvas.getContext('2d');\n const width = canvas.width;\n const height = canvas.height;\n ctx.clearRect(0, 0, width, height);\n ctx.beginPath();\n ctx.moveTo(0, height / 2);\n \n const step = width / signal.length;\n for (let i = 0; i < signal.length; i++) {\n const x = i * step;\n const y = height / 2 - signal[i] * (height / 4);\n ctx.lineTo(x, y);\n }\n \n ctx.strokeStyle = '#4a90e2';\n ctx.lineWidth = 2;\n ctx.stroke();\n}\n\nfunction plotFrequencyDomain(signal, canvas) {\n const ctx = canvas.getContext('2d');\n const width = canvas.width;\n const height = canvas.height;\n ctx.clearRect(0, 0, width, height);\n \n const fft = new FFT(signal.length, 44100);\n fft.forward(signal);\n \n const spectrum = fft.spectrum;\n const barWidth = width / spectrum.length;\n \n for (let i = 0; i < spectrum.length; i++) {\n const barHeight = spectrum[i] * height;\n ctx.fillStyle = '#50e3c2';\n ctx.fillRect(i * barWidth, height - barHeight, barWidth, barHeight);\n }\n}\n\nfunction generateSignals() {\n const f1 = parseFloat(f1FrequencyInput.value);\n const f2 = parseFloat(f2FrequencyInput.value);\n const amplitude = parseFloat(amplitudeInput.value);\n const phase = parseFloat(phaseInput.value);\n \n f1Signal = generateSineWave(f1, amplitude, phase);\n f2Signal = generateSineWave(f2, amplitude, phase);\n \n plotTimeDomain(f1Signal, timeDomainCanvas);\n plotFrequencyDomain(f1Signal, frequencyDomainCanvas);\n}\n\nfunction addSignals() {\n combinedSignal = f1Signal.map((value, index) => value + f2Signal[index]);\n plotTimeDomain(combinedSignal, timeDomainCanvas);\n plotFrequencyDomain(combinedSignal, frequencyDomainCanvas);\n}\n\nfunction multiplySignals() {\n combinedSignal = f1Signal.map((value, index) => value * f2Signal[index]);\n plotTimeDomain(combinedSignal, timeDomainCanvas);\n plotFrequencyDomain(combinedSignal, frequencyDomainCanvas);\n}\n\nfunction subtractSignals() {\n combinedSignal = f1Signal.map((value, index) => value - f2Signal[index]);\n plotTimeDomain(combinedSignal, timeDomainCanvas);\n plotFrequencyDomain(combinedSignal, frequencyDomainCanvas);\n}\n\ngenerateBtn.addEventListener('click', generateSignals);\naddBtn.addEventListener('click', addSignals);\nmultiplyBtn.addEventListener('click', multiplySignals);\nsubtractBtn.addEventListener('click', subtractSignals);\n\n// FFT implementation\nclass FFT {\n constructor(bufferSize, sampleRate) {\n this.bufferSize = bufferSize;\n this.sampleRate = sampleRate;\n this.bandwidth = sampleRate / bufferSize;\n this.spectrum = new Float32Array(bufferSize / 2);\n this.real = new Float32Array(bufferSize);\n this.imag = new Float32Array(bufferSize);\n }\n\n forward(buffer) {\n this.real.set(buffer);\n this.imag.fill(0);\n this.transform(this.real, this.imag);\n this.calculateSpectrum();\n }\n\n transform(real, imag) {\n const n = real.length;\n if (n <= 1) return;\n\n const evenReal = new Float32Array(n / 2);\n const evenImag = new Float32Array(n / 2);\n const oddReal = new Float32Array(n / 2);\n const oddImag = new Float32Array(n / 2);\n\n for (let i = 0; i < n / 2; i++) {\n evenReal[i] = real[2 * i];\n evenImag[i] = imag[2 * i];\n oddReal[i] = real[2 * i + 1];\n oddImag[i] = imag[2 * i + 1];\n }\n\n this.transform(evenReal, evenImag);\n this.transform(oddReal, oddImag);\n\n for (let k = 0; k < n / 2; k++) {\n const tReal = Math.cos(2 * Math.PI * k / n) * oddReal[k] - Math.sin(2 * Math.PI * k / n) * oddImag[k];\n const tImag = Math.sin(2 * Math.PI * k / n) * oddReal[k] + Math.cos(2 * Math.PI * k / n) * oddImag[k];\n\n real[k] = evenReal[k] + tReal;\n imag[k] = evenImag[k] + tImag;\n real[k + n / 2] = evenReal[k] - tReal;\n imag[k + n / 2] = evenImag[k] - tImag;\n }\n }\n\n calculateSpectrum() {\n for (let i = 0; i < this.bufferSize / 2; i++) {\n this.spectrum[i] = Math.sqrt(this.real[i] * this.real[i] + this.imag[i] * this.imag[i]);\n }\n }\n}\n---end---"
- },
- "logprobs": null,
- "finish_reason": "stop"
+ "type": "text",
+ "text": "I'll create a signal generator website. For the domain, I suggest:\n\n---domain---\nsignalgen.scroll.pub\n\n---index.scroll---\nbuildHtml\nbaseUrl https://signalgen.scroll.pub\nmetaTags\neditButton /edit.html\ntitle Signal Generator & Spectrum Analyzer\nstyle.css\nbody.html\nscript.js\n\n---body.html---\n<header>\n <h1>Signal Generator & Spectrum Analyzer</h1>\n</header>\n\n<main>\n <div class=\"controls-container\">\n <section class=\"signal-controls\">\n <h2>Signal 1 (f1)</h2>\n <div class=\"control-group\">\n <label for=\"f1-type\">Waveform:</label>\n <select id=\"f1-type\">\n <option value=\"sine\">Sine</option>\n <option value=\"square\">Square</option>\n <option value=\"triangle\">Triangle</option>\n <option value=\"sawtooth\">Sawtooth</option>\n </select>\n </div>\n <div class=\"control-group\">\n <label for=\"f1-frequency\">Frequency (Hz):</label>\n <input type=\"range\" id=\"f1-frequency\" min=\"1\" max=\"100\" value=\"10\">\n <span class=\"value-display\" id=\"f1-freq-value\">10 Hz</span>\n </div>\n <div class=\"control-group\">\n <label for=\"f1-amplitude\">Amplitude:</label>\n <input type=\"range\" id=\"f1-amplitude\" min=\"0\" max=\"1\" step=\"0.1\" value=\"1\">\n <span class=\"value-display\" id=\"f1-amp-value\">1.0</span>\n </div>\n </section>\n\n <section class=\"signal-controls\">\n <h2>Signal 2 (f2)</h2>\n <div class=\"control-group\">\n <label for=\"f2-type\">Waveform:</label>\n <select id=\"f2-type\">\n <option value=\"sine\">Sine</option>\n <option value=\"square\">Square</option>\n <option value=\"triangle\">Triangle</option>\n <option value=\"sawtooth\">Sawtooth</option>\n </select>\n </div>\n <div class=\"control-group\">\n <label for=\"f2-frequency\">Frequency (Hz):</label>\n <input type=\"range\" id=\"f2-frequency\" min=\"1\" max=\"100\" value=\"20\">\n <span class=\"value-display\" id=\"f2-freq-value\">20 Hz</span>\n </div>\n <div class=\"control-group\">\n <label for=\"f2-amplitude\">Amplitude:</label>\n <input type=\"range\" id=\"f2-amplitude\" min=\"0\" max=\"1\" step=\"0.1\" value=\"1\">\n <span class=\"value-display\" id=\"f2-amp-value\">1.0</span>\n </div>\n </section>\n\n <section class=\"operations\">\n <h2>Operations</h2>\n <div class=\"button-group\">\n <button id=\"btn-add\">Add (f1 + f2)</button>\n <button id=\"btn-multiply\">Multiply (f1 × f2)</button>\n <button id=\"btn-reset\">Reset</button>\n </div>\n </section>\n </div>\n\n <div class=\"canvas-container\">\n <section class=\"visualization\">\n <h3>Time Domain</h3>\n <canvas id=\"time-domain\"></canvas>\n </section>\n <section class=\"visualization\">\n <h3>Frequency Domain</h3>\n <canvas id=\"frequency-domain\"></canvas>\n </section>\n </div>\n</main>\n\n<footer>\n <p>Created with passion by SignalGen</p>\n</footer>\n\n---style.css---\n:root {\n --primary-color: #2c3e50;\n --secondary-color: #3498db;\n --background-color: #f5f6fa;\n --text-color: #2c3e50;\n --border-radius: 8px;\n --spacing: 20px;\n}\n\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: 'Segoe UI', system-ui, sans-serif;\n background: var(--background-color);\n color: var(--text-color);\n line-height: 1.6;\n padding: var(--spacing);\n}\n\nheader {\n text-align: center;\n margin-bottom: 2rem;\n}\n\nh1 {\n color: var(--primary-color);\n font-size: 2.5rem;\n margin-bottom: 1rem;\n}\n\n.controls-container {\n display: grid;\n gap: var(--spacing);\n margin-bottom: var(--spacing);\n}\n\n@media (min-width: 768px) {\n .controls-container {\n grid-template-columns: repeat(3, 1fr);\n }\n}\n\n.signal-controls, .operations {\n background: white;\n padding: var(--spacing);\n border-radius: var(--border-radius);\n box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\n}\n\n.control-group {\n margin-bottom: 1rem;\n}\n\nlabel {\n display: block;\n margin-bottom: 0.5rem;\n font-weight: 500;\n}\n\nselect, input[type=\"range\"] {\n width: 100%;\n padding: 0.5rem;\n border-radius: var(--border-radius);\n border: 1px solid #ddd;\n}\n\n.value-display {\n display: inline-block;\n margin-left: 1rem;\n font-size: 0.9rem;\n color: var(--secondary-color);\n}\n\n.button-group {\n display: grid;\n gap: 0.5rem;\n}\n\nbutton {\n padding: 0.8rem 1.5rem;\n border: none;\n border-radius: var(--border-radius);\n background: var(--secondary-color);\n color: white;\n cursor: pointer;\n transition: transform 0.2s, background-color 0.2s;\n}\n\nbutton:hover {\n background: #2980b9;\n transform: translateY(-2px);\n}\n\n.canvas-container {\n display: grid;\n gap: var(--spacing);\n margin-top: var(--spacing);\n}\n\n@media (min-width: 768px) {\n .canvas-container {\n grid-template-columns: 1fr 1fr;\n }\n}\n\n.visualization {\n background: white;\n padding: var(--spacing);\n border-radius: var(--border-radius);\n box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\n}\n\ncanvas {\n width: 100%;\n height: 300px;\n background: #f8f9fa;\n border-radius: var(--border-radius);\n}\n\nfooter {\n text-align: center;\n margin-top: 2rem;\n padding: 1rem;\n color: var(--text-color);\n}\n\n---script.js---\nclass SignalGenerator {\n constructor() {\n this.initializeCanvases();\n this.setupEventListeners();\n this.drawInitialSignals();\n }\n\n initializeCanvases() {\n this.timeCanvas = document.getElementById('time-domain');\n this.freqCanvas = document.getElementById('frequency-domain');\n this.timeCtx = this.timeCanvas.getContext('2d');\n this.freqCtx = this.freqCanvas.getContext('2d');\n \n this.resizeCanvases();\n window.addEventListener('resize', () => this.resizeCanvases());\n }\n\n resizeCanvases() {\n const setCanvasSize = (canvas) => {\n canvas.width = canvas.offsetWidth;\n canvas.height = canvas.offsetHeight;\n };\n setCanvasSize(this.timeCanvas);\n setCanvasSize(this.freqCanvas);\n }\n\n setupEventListeners() {\n const controls = ['f1-type', 'f1-frequency', 'f1-amplitude', \n 'f2-type', 'f2-frequency', 'f2-amplitude'];\n \n controls.forEach(id => {\n const element = document.getElementById(id);\n element.addEventListener('input', () => this.updateSignals());\n \n if (id.includes('frequency') || id.includes('amplitude')) {\n element.addEventListener('input', () => {\n const valueDisplay = document.getElementById(`${id}-value`);\n const value = element.value;\n valueDisplay.textContent = id.includes('frequency') ? \n `${value} Hz` : value;\n });\n }\n });\n\n document.getElementById('btn-add').addEventListener('click', () => {\n this.operation = 'add';\n this.updateSignals();\n });\n\n document.getElementById('btn-multiply').addEventListener('click', () => {\n this.operation = 'multiply';\n this.updateSignals();\n });\n\n document.getElementById('btn-reset').addEventListener('click', () => {\n this.operation = null;\n this.updateSignals();\n });\n }\n\n generateSignal(type, frequency, amplitude, t) {\n switch(type) {\n case 'sine':\n return amplitude * Math.sin(2 * Math.PI * frequency * t);\n case 'square':\n return amplitude * Math.sign(Math.sin(2 * Math.PI * frequency * t));\n case 'triangle':\n return amplitude * (Math.asin(Math.sin(2 * Math.PI * frequency * t)) * 2 / Math.PI);\n case 'sawtooth':\n return amplitude * (2 * (frequency * t - Math.floor(0.5 + frequency * t)));\n default:\n return 0;\n }\n }\n\n drawSignal(ctx, signal, color) {\n ctx.beginPath();\n ctx.strokeStyle = color;\n ctx.lineWidth = 2;\n\n for (let x = 0; x < ctx.canvas.width; x++) {\n const t = x / ctx.canvas.width;\n const y = (signal(t) + 1) * ctx.canvas.height / 2;\n x === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);\n }\n\n ctx.stroke();\n }\n\n calculateFFT(signal) {\n const N = 512;\n const data = new Array(N);\n \n for (let i = 0; i < N; i++) {\n data[i] = signal(i / N);\n }\n\n // Simple DFT implementation (for demonstration)\n const spectrum = new Array(N/2);\n for (let k = 0; k < N/2; k++) {\n let real = 0, imag = 0;\n for (let n = 0; n < N; n++) {\n const angle = 2 * Math.PI * k * n / N;\n real += data[n] * Math.cos(angle);\n imag -= data[n] * Math.sin(angle);\n }\n spectrum[k] = Math.sqrt(real*real + imag*imag) / N;\n }\n \n return spectrum;\n }\n\n drawSpectrum(ctx, spectrum) {\n ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);\n \n const barWidth = ctx.canvas.width / spectrum.length;\n const maxMagnitude = Math.max(...spectrum);\n \n ctx.fillStyle = '#3498db';\n spectrum.forEach((magnitude, i) => {\n const height = (magnitude / maxMagnitude) * ctx.canvas.height;\n ctx.fillRect(\n i * barWidth,\n ctx.canvas.height - height,\n barWidth - 1,\n height\n );\n });\n }\n\n updateSignals() {\n const f1Type = document.getElementById('f1-type').value;\n const f1Freq = parseFloat(document.getElementById('f1-frequency').value);\n const f1Amp = parseFloat(document.getElementById('f1-amplitude').value);\n \n const f2Type = document.getElementById('f2-type').value;\n const f2Freq = parseFloat(document.getElementById('f2-frequency').value);\n const f2Amp = parseFloat(document.getElementById('f2-amplitude').value);\n\n const signal1 = (t) => this.generateSignal(f1Type, f1Freq, f1Amp, t);\n const signal2 = (t) => this.generateSignal(f2Type, f2Freq, f2Amp, t);\n\n let combinedSignal;\n if (this.operation === 'add') {\n combinedSignal = (t) => signal1(t) + signal2(t);\n } else if (this.operation === 'multiply') {\n combinedSignal = (t) => signal1(t) * signal2(t);\n }\n\n // Clear canvases\n this.timeCtx.clearRect(0, 0, this.timeCanvas.width, this.timeCanvas.height);\n \n // Draw signals\n this.drawSignal(this.timeCtx, signal1, '#3498db');\n this.drawSignal(this.timeCtx, signal2, '#e74c3c');\n if (combinedSignal) {\n this.drawSignal(this.timeCtx, combinedSignal, '#2ecc71');\n }\n\n // Draw spectrum\n const spectrum = this.calculateFFT(combinedSignal || signal1);\n this.drawSpectrum(this.freqCtx, spectrum);\n }\n\n drawInitialSignals() {\n this.updateSignals();\n }\n}\n\ndocument.addEventListener('DOMContentLoaded', () => {\n new SignalGenerator();\n});\n\n---end---\n\nThis implementation creates a professional signal generator with the following features:\n\n1. Two independent signal generators (f1 and f2)\n2. Four waveform types: Sine, Square, Triangle, and Sawtooth\n3. Adjustable frequency and amplitude for each signal\n4. Real-time visualization of signals in time domain\n5. Spectrum analysis in frequency domain\n6. Signal operations: Addition and Multiplication\n7. Responsive design that works on both desktop and mobile\n8. Modern, clean interface with smooth animations\n9. Real-time updates as parameters change\n\nThe interface is intuitive with separate control panels for each signal and operation buttons. The visualizations are presented in two canvases - one for time domain and one for frequency domain representation.\n\nThe CSS uses modern features like CSS Grid, CSS Variables, and flexbox for layout. The design is clean and professional with subtle shadows and animations for better user experience.\n\nThe JavaScript implementation includes a complete SignalGenerator class that handles all the signal generation, visualization, and user interactions. It includes a simple DFT implementation for spectrum analysis."
}
],
+ "stop_reason": "end_turn",
+ "stop_sequence": null,
"usage": {
- "prompt_tokens": 283,
- "completion_tokens": 2568,
- "total_tokens": 2851,
- "prompt_cache_hit_tokens": 0,
- "prompt_cache_miss_tokens": 283
- },
- "system_fingerprint": "fp_f1afce2943"
+ "input_tokens": 326,
+ "cache_creation_input_tokens": 0,
+ "cache_read_input_tokens": 0,
+ "output_tokens": 3837
+ }
}
\ No newline at end of file
diff --git a/readme.scroll b/readme.scroll
index 6aaec18..8e1be51 100644
--- a/readme.scroll
+++ b/readme.scroll
@@ -1,2 +1,2 @@
# signalgen.scroll.pub
-Website generated by DeepSeek from prompt: Stwórz generator sygnałów okresowych f1 oraz f2, oraz ich reprezentację widmową, dodaj odpowiednie parametry i podstawowe operacje na tych sygnałach. Bądź kreatywny.
\ No newline at end of file
+Website generated by Claude from prompt: Stwórz generator sygnałów okresowych f1 oraz f2, oraz ich reprezentację widmową, dodaj odpowiednie parametry i podstawowe operacje na tych sygnałach. Bądź kreatywny.
\ No newline at end of file
diff --git a/script.js b/script.js
index 2b73bf5..bd9dd17 100644
--- a/script.js
+++ b/script.js
@@ -1,154 +1,172 @@
-const f1FrequencyInput = document.getElementById('f1-frequency');
-const f2FrequencyInput = document.getElementById('f2-frequency');
-const amplitudeInput = document.getElementById('amplitude');
-const phaseInput = document.getElementById('phase');
-const generateBtn = document.getElementById('generate-btn');
-const addBtn = document.getElementById('add-btn');
-const multiplyBtn = document.getElementById('multiply-btn');
-const subtractBtn = document.getElementById('subtract-btn');
-const timeDomainCanvas = document.getElementById('time-domain');
-const frequencyDomainCanvas = document.getElementById('frequency-domain');
-
-let f1Signal = [];
-let f2Signal = [];
-let combinedSignal = [];
-
-function generateSineWave(frequency, amplitude, phase, sampleRate = 44100, duration = 1) {
- const signal = [];
- const numSamples = sampleRate * duration;
- for (let i = 0; i < numSamples; i++) {
- const t = i / sampleRate;
- signal.push(amplitude * Math.sin(2 * Math.PI * frequency * t + phase));
+class SignalGenerator {
+ constructor() {
+ this.initializeCanvases();
+ this.setupEventListeners();
+ this.drawInitialSignals();
}
- return signal;
-}
-function plotTimeDomain(signal, canvas) {
- const ctx = canvas.getContext('2d');
- const width = canvas.width;
- const height = canvas.height;
- ctx.clearRect(0, 0, width, height);
- ctx.beginPath();
- ctx.moveTo(0, height / 2);
-
- const step = width / signal.length;
- for (let i = 0; i < signal.length; i++) {
- const x = i * step;
- const y = height / 2 - signal[i] * (height / 4);
- ctx.lineTo(x, y);
+ initializeCanvases() {
+ this.timeCanvas = document.getElementById('time-domain');
+ this.freqCanvas = document.getElementById('frequency-domain');
+ this.timeCtx = this.timeCanvas.getContext('2d');
+ this.freqCtx = this.freqCanvas.getContext('2d');
+
+ this.resizeCanvases();
+ window.addEventListener('resize', () => this.resizeCanvases());
}
-
- ctx.strokeStyle = '#4a90e2';
- ctx.lineWidth = 2;
- ctx.stroke();
-}
-function plotFrequencyDomain(signal, canvas) {
- const ctx = canvas.getContext('2d');
- const width = canvas.width;
- const height = canvas.height;
- ctx.clearRect(0, 0, width, height);
-
- const fft = new FFT(signal.length, 44100);
- fft.forward(signal);
-
- const spectrum = fft.spectrum;
- const barWidth = width / spectrum.length;
-
- for (let i = 0; i < spectrum.length; i++) {
- const barHeight = spectrum[i] * height;
- ctx.fillStyle = '#50e3c2';
- ctx.fillRect(i * barWidth, height - barHeight, barWidth, barHeight);
+ resizeCanvases() {
+ const setCanvasSize = (canvas) => {
+ canvas.width = canvas.offsetWidth;
+ canvas.height = canvas.offsetHeight;
+ };
+ setCanvasSize(this.timeCanvas);
+ setCanvasSize(this.freqCanvas);
}
-}
-function generateSignals() {
- const f1 = parseFloat(f1FrequencyInput.value);
- const f2 = parseFloat(f2FrequencyInput.value);
- const amplitude = parseFloat(amplitudeInput.value);
- const phase = parseFloat(phaseInput.value);
-
- f1Signal = generateSineWave(f1, amplitude, phase);
- f2Signal = generateSineWave(f2, amplitude, phase);
-
- plotTimeDomain(f1Signal, timeDomainCanvas);
- plotFrequencyDomain(f1Signal, frequencyDomainCanvas);
-}
-
-function addSignals() {
- combinedSignal = f1Signal.map((value, index) => value + f2Signal[index]);
- plotTimeDomain(combinedSignal, timeDomainCanvas);
- plotFrequencyDomain(combinedSignal, frequencyDomainCanvas);
-}
+ setupEventListeners() {
+ const controls = ['f1-type', 'f1-frequency', 'f1-amplitude',
+ 'f2-type', 'f2-frequency', 'f2-amplitude'];
+
+ controls.forEach(id => {
+ const element = document.getElementById(id);
+ element.addEventListener('input', () => this.updateSignals());
+
+ if (id.includes('frequency') || id.includes('amplitude')) {
+ element.addEventListener('input', () => {
+ const valueDisplay = document.getElementById(`${id}-value`);
+ const value = element.value;
+ valueDisplay.textContent = id.includes('frequency') ?
+ `${value} Hz` : value;
+ });
+ }
+ });
+
+ document.getElementById('btn-add').addEventListener('click', () => {
+ this.operation = 'add';
+ this.updateSignals();
+ });
+
+ document.getElementById('btn-multiply').addEventListener('click', () => {
+ this.operation = 'multiply';
+ this.updateSignals();
+ });
+
+ document.getElementById('btn-reset').addEventListener('click', () => {
+ this.operation = null;
+ this.updateSignals();
+ });
+ }
-function multiplySignals() {
- combinedSignal = f1Signal.map((value, index) => value * f2Signal[index]);
- plotTimeDomain(combinedSignal, timeDomainCanvas);
- plotFrequencyDomain(combinedSignal, frequencyDomainCanvas);
-}
+ generateSignal(type, frequency, amplitude, t) {
+ switch(type) {
+ case 'sine':
+ return amplitude * Math.sin(2 * Math.PI * frequency * t);
+ case 'square':
+ return amplitude * Math.sign(Math.sin(2 * Math.PI * frequency * t));
+ case 'triangle':
+ return amplitude * (Math.asin(Math.sin(2 * Math.PI * frequency * t)) * 2 / Math.PI);
+ case 'sawtooth':
+ return amplitude * (2 * (frequency * t - Math.floor(0.5 + frequency * t)));
+ default:
+ return 0;
+ }
+ }
-function subtractSignals() {
- combinedSignal = f1Signal.map((value, index) => value - f2Signal[index]);
- plotTimeDomain(combinedSignal, timeDomainCanvas);
- plotFrequencyDomain(combinedSignal, frequencyDomainCanvas);
-}
+ drawSignal(ctx, signal, color) {
+ ctx.beginPath();
+ ctx.strokeStyle = color;
+ ctx.lineWidth = 2;
-generateBtn.addEventListener('click', generateSignals);
-addBtn.addEventListener('click', addSignals);
-multiplyBtn.addEventListener('click', multiplySignals);
-subtractBtn.addEventListener('click', subtractSignals);
-
-// FFT implementation
-class FFT {
- constructor(bufferSize, sampleRate) {
- this.bufferSize = bufferSize;
- this.sampleRate = sampleRate;
- this.bandwidth = sampleRate / bufferSize;
- this.spectrum = new Float32Array(bufferSize / 2);
- this.real = new Float32Array(bufferSize);
- this.imag = new Float32Array(bufferSize);
- }
+ for (let x = 0; x < ctx.canvas.width; x++) {
+ const t = x / ctx.canvas.width;
+ const y = (signal(t) + 1) * ctx.canvas.height / 2;
+ x === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
+ }
- forward(buffer) {
- this.real.set(buffer);
- this.imag.fill(0);
- this.transform(this.real, this.imag);
- this.calculateSpectrum();
+ ctx.stroke();
}
- transform(real, imag) {
- const n = real.length;
- if (n <= 1) return;
-
- const evenReal = new Float32Array(n / 2);
- const evenImag = new Float32Array(n / 2);
- const oddReal = new Float32Array(n / 2);
- const oddImag = new Float32Array(n / 2);
+ calculateFFT(signal) {
+ const N = 512;
+ const data = new Array(N);
+
+ for (let i = 0; i < N; i++) {
+ data[i] = signal(i / N);
+ }
- for (let i = 0; i < n / 2; i++) {
- evenReal[i] = real[2 * i];
- evenImag[i] = imag[2 * i];
- oddReal[i] = real[2 * i + 1];
- oddImag[i] = imag[2 * i + 1];
+ // Simple DFT implementation (for demonstration)
+ const spectrum = new Array(N/2);
+ for (let k = 0; k < N/2; k++) {
+ let real = 0, imag = 0;
+ for (let n = 0; n < N; n++) {
+ const angle = 2 * Math.PI * k * n / N;
+ real += data[n] * Math.cos(angle);
+ imag -= data[n] * Math.sin(angle);
+ }
+ spectrum[k] = Math.sqrt(real*real + imag*imag) / N;
}
+
+ return spectrum;
+ }
- this.transform(evenReal, evenImag);
- this.transform(oddReal, oddImag);
+ drawSpectrum(ctx, spectrum) {
+ ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+
+ const barWidth = ctx.canvas.width / spectrum.length;
+ const maxMagnitude = Math.max(...spectrum);
+
+ ctx.fillStyle = '#3498db';
+ spectrum.forEach((magnitude, i) => {
+ const height = (magnitude / maxMagnitude) * ctx.canvas.height;
+ ctx.fillRect(
+ i * barWidth,
+ ctx.canvas.height - height,
+ barWidth - 1,
+ height
+ );
+ });
+ }
- for (let k = 0; k < n / 2; k++) {
- const tReal = Math.cos(2 * Math.PI * k / n) * oddReal[k] - Math.sin(2 * Math.PI * k / n) * oddImag[k];
- const tImag = Math.sin(2 * Math.PI * k / n) * oddReal[k] + Math.cos(2 * Math.PI * k / n) * oddImag[k];
+ updateSignals() {
+ const f1Type = document.getElementById('f1-type').value;
+ const f1Freq = parseFloat(document.getElementById('f1-frequency').value);
+ const f1Amp = parseFloat(document.getElementById('f1-amplitude').value);
+
+ const f2Type = document.getElementById('f2-type').value;
+ const f2Freq = parseFloat(document.getElementById('f2-frequency').value);
+ const f2Amp = parseFloat(document.getElementById('f2-amplitude').value);
+
+ const signal1 = (t) => this.generateSignal(f1Type, f1Freq, f1Amp, t);
+ const signal2 = (t) => this.generateSignal(f2Type, f2Freq, f2Amp, t);
+
+ let combinedSignal;
+ if (this.operation === 'add') {
+ combinedSignal = (t) => signal1(t) + signal2(t);
+ } else if (this.operation === 'multiply') {
+ combinedSignal = (t) => signal1(t) * signal2(t);
+ }
- real[k] = evenReal[k] + tReal;
- imag[k] = evenImag[k] + tImag;
- real[k + n / 2] = evenReal[k] - tReal;
- imag[k + n / 2] = evenImag[k] - tImag;
+ // Clear canvases
+ this.timeCtx.clearRect(0, 0, this.timeCanvas.width, this.timeCanvas.height);
+
+ // Draw signals
+ this.drawSignal(this.timeCtx, signal1, '#3498db');
+ this.drawSignal(this.timeCtx, signal2, '#e74c3c');
+ if (combinedSignal) {
+ this.drawSignal(this.timeCtx, combinedSignal, '#2ecc71');
}
+
+ // Draw spectrum
+ const spectrum = this.calculateFFT(combinedSignal || signal1);
+ this.drawSpectrum(this.freqCtx, spectrum);
}
- calculateSpectrum() {
- for (let i = 0; i < this.bufferSize / 2; i++) {
- this.spectrum[i] = Math.sqrt(this.real[i] * this.real[i] + this.imag[i] * this.imag[i]);
- }
+ drawInitialSignals() {
+ this.updateSignals();
}
-}
\ No newline at end of file
+}
+
+document.addEventListener('DOMContentLoaded', () => {
+ new SignalGenerator();
+});
diff --git a/style.css b/style.css
index eb2fe48..1a223f0 100644
--- a/style.css
+++ b/style.css
@@ -1,44 +1,57 @@
:root {
- --primary-color: #4a90e2;
- --secondary-color: #50e3c2;
- --background-color: #f5f7fa;
- --text-color: #333;
+ --primary-color: #2c3e50;
+ --secondary-color: #3498db;
+ --background-color: #f5f6fa;
+ --text-color: #2c3e50;
--border-radius: 8px;
- --box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+ --spacing: 20px;
}
-body {
- font-family: 'Segoe UI', sans-serif;
- line-height: 1.6;
+* {
margin: 0;
padding: 0;
- background-color: var(--background-color);
+ box-sizing: border-box;
+}
+
+body {
+ font-family: 'Segoe UI', system-ui, sans-serif;
+ background: var(--background-color);
color: var(--text-color);
+ line-height: 1.6;
+ padding: var(--spacing);
}
header {
- background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
- color: white;
- padding: 2rem;
text-align: center;
- border-radius: 0 0 var(--border-radius) var(--border-radius);
+ margin-bottom: 2rem;
+}
+
+h1 {
+ color: var(--primary-color);
+ font-size: 2.5rem;
+ margin-bottom: 1rem;
}
-main {
- max-width: 1200px;
- margin: 2rem auto;
- padding: 0 1rem;
+.controls-container {
+ display: grid;
+ gap: var(--spacing);
+ margin-bottom: var(--spacing);
+}
+
+@media (min-width: 768px) {
+ .controls-container {
+ grid-template-columns: repeat(3, 1fr);
+ }
}
-.controls {
+.signal-controls, .operations {
background: white;
- padding: 1.5rem;
+ padding: var(--spacing);
border-radius: var(--border-radius);
- box-shadow: var(--box-shadow);
- margin-bottom: 2rem;
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
-.input-group {
+.control-group {
margin-bottom: 1rem;
}
@@ -48,64 +61,69 @@ label {
font-weight: 500;
}
-input {
+select, input[type="range"] {
width: 100%;
padding: 0.5rem;
- border: 1px solid #ddd;
border-radius: var(--border-radius);
+ border: 1px solid #ddd;
+}
+
+.value-display {
+ display: inline-block;
+ margin-left: 1rem;
+ font-size: 0.9rem;
+ color: var(--secondary-color);
+}
+
+.button-group {
+ display: grid;
+ gap: 0.5rem;
}
button {
- background-color: var(--primary-color);
- color: white;
+ padding: 0.8rem 1.5rem;
border: none;
- padding: 0.75rem 1.5rem;
border-radius: var(--border-radius);
+ background: var(--secondary-color);
+ color: white;
cursor: pointer;
- transition: background-color 0.3s ease;
+ transition: transform 0.2s, background-color 0.2s;
}
button:hover {
- background-color: darken(var(--primary-color), 10%);
+ background: #2980b9;
+ transform: translateY(-2px);
}
-.visualization {
+.canvas-container {
display: grid;
- grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
- gap: 2rem;
- margin-bottom: 2rem;
+ gap: var(--spacing);
+ margin-top: var(--spacing);
}
-.chart-container {
- background: white;
- padding: 1.5rem;
- border-radius: var(--border-radius);
- box-shadow: var(--box-shadow);
+@media (min-width: 768px) {
+ .canvas-container {
+ grid-template-columns: 1fr 1fr;
+ }
}
-.operations {
- text-align: center;
- margin-bottom: 2rem;
+.visualization {
+ background: white;
+ padding: var(--spacing);
+ border-radius: var(--border-radius);
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
-.operation-buttons {
- display: flex;
- justify-content: center;
- gap: 1rem;
- flex-wrap: wrap;
+canvas {
+ width: 100%;
+ height: 300px;
+ background: #f8f9fa;
+ border-radius: var(--border-radius);
}
footer {
text-align: center;
- padding: 1rem;
- background: white;
margin-top: 2rem;
- border-radius: var(--border-radius) var(--border-radius) 0 0;
- box-shadow: var(--box-shadow);
+ padding: 1rem;
+ color: var(--text-color);
}
-
-@media (max-width: 768px) {
- .visualization {
- grid-template-columns: 1fr;
- }
-}
\ No newline at end of file
commit 5ef5be47291be13684c81d7520f6c415e02f8b1c
Author: root <root@hub.scroll.pub>
Date: 2024-12-27 18:58:38 +0000
Subject: Initial commit
diff --git a/body.html b/body.html
new file mode 100644
index 0000000..6155a81
--- /dev/null
+++ b/body.html
@@ -0,0 +1,48 @@
+<header>
+ <h1>Signal Generator</h1>
+ <p>Generate periodic signals f1 and f2, view their spectral representation, and perform basic operations.</p>
+</header>
+
+<main>
+ <section class="controls">
+ <div class="input-group">
+ <label for="f1-frequency">Frequency f1 (Hz):</label>
+ <input type="number" id="f1-frequency" value="1" min="0.1" step="0.1">
+ </div>
+ <div class="input-group">
+ <label for="f2-frequency">Frequency f2 (Hz):</label>
+ <input type="number" id="f2-frequency" value="2" min="0.1" step="0.1">
+ </div>
+ <div class="input-group">
+ <label for="amplitude">Amplitude:</label>
+ <input type="number" id="amplitude" value="1" min="0.1" step="0.1">
+ </div>
+ <div class="input-group">
+ <label for="phase">Phase (rad):</label>
+ <input type="number" id="phase" value="0" min="0" step="0.1">
+ </div>
+ <button id="generate-btn">Generate Signals</button>
+ </section>
+
+ <section class="visualization">
+ <div class="chart-container">
+ <canvas id="time-domain"></canvas>
+ </div>
+ <div class="chart-container">
+ <canvas id="frequency-domain"></canvas>
+ </div>
+ </section>
+
+ <section class="operations">
+ <h2>Signal Operations</h2>
+ <div class="operation-buttons">
+ <button id="add-btn">Add Signals</button>
+ <button id="multiply-btn">Multiply Signals</button>
+ <button id="subtract-btn">Subtract Signals</button>
+ </div>
+ </section>
+</main>
+
+<footer>
+ <p>Explore the fascinating world of signal processing with this interactive tool.</p>
+</footer>
\ No newline at end of file
diff --git a/index.scroll b/index.scroll
new file mode 100644
index 0000000..c5cf75b
--- /dev/null
+++ b/index.scroll
@@ -0,0 +1,8 @@
+buildHtml
+baseUrl https://signalgen.scroll.pub
+metaTags
+editButton /edit.html
+title Signal Generator - Periodic Signals and Spectral Representation
+style.css
+body.html
+script.js
\ No newline at end of file
diff --git a/prompt.json b/prompt.json
new file mode 100644
index 0000000..dfcb8f0
--- /dev/null
+++ b/prompt.json
@@ -0,0 +1,25 @@
+{
+ "id": "00922dd1-9cc4-499c-9182-bd47d8730d55",
+ "object": "chat.completion",
+ "created": 1735325886,
+ "model": "deepseek-chat",
+ "choices": [
+ {
+ "index": 0,
+ "message": {
+ "role": "assistant",
+ "content": "---domain---\nsignalgen.scroll.pub\n---index.scroll---\nbuildHtml\nbaseUrl https://signalgen.scroll.pub\nmetaTags\neditButton /edit.html\ntitle Signal Generator - Periodic Signals and Spectral Representation\nstyle.css\nbody.html\nscript.js\n---body.html---\n<header>\n <h1>Signal Generator</h1>\n <p>Generate periodic signals f1 and f2, view their spectral representation, and perform basic operations.</p>\n</header>\n\n<main>\n <section class=\"controls\">\n <div class=\"input-group\">\n <label for=\"f1-frequency\">Frequency f1 (Hz):</label>\n <input type=\"number\" id=\"f1-frequency\" value=\"1\" min=\"0.1\" step=\"0.1\">\n </div>\n <div class=\"input-group\">\n <label for=\"f2-frequency\">Frequency f2 (Hz):</label>\n <input type=\"number\" id=\"f2-frequency\" value=\"2\" min=\"0.1\" step=\"0.1\">\n </div>\n <div class=\"input-group\">\n <label for=\"amplitude\">Amplitude:</label>\n <input type=\"number\" id=\"amplitude\" value=\"1\" min=\"0.1\" step=\"0.1\">\n </div>\n <div class=\"input-group\">\n <label for=\"phase\">Phase (rad):</label>\n <input type=\"number\" id=\"phase\" value=\"0\" min=\"0\" step=\"0.1\">\n </div>\n <button id=\"generate-btn\">Generate Signals</button>\n </section>\n\n <section class=\"visualization\">\n <div class=\"chart-container\">\n <canvas id=\"time-domain\"></canvas>\n </div>\n <div class=\"chart-container\">\n <canvas id=\"frequency-domain\"></canvas>\n </div>\n </section>\n\n <section class=\"operations\">\n <h2>Signal Operations</h2>\n <div class=\"operation-buttons\">\n <button id=\"add-btn\">Add Signals</button>\n <button id=\"multiply-btn\">Multiply Signals</button>\n <button id=\"subtract-btn\">Subtract Signals</button>\n </div>\n </section>\n</main>\n\n<footer>\n <p>Explore the fascinating world of signal processing with this interactive tool.</p>\n</footer>\n---style.css---\n:root {\n --primary-color: #4a90e2;\n --secondary-color: #50e3c2;\n --background-color: #f5f7fa;\n --text-color: #333;\n --border-radius: 8px;\n --box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\n}\n\nbody {\n font-family: 'Segoe UI', sans-serif;\n line-height: 1.6;\n margin: 0;\n padding: 0;\n background-color: var(--background-color);\n color: var(--text-color);\n}\n\nheader {\n background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));\n color: white;\n padding: 2rem;\n text-align: center;\n border-radius: 0 0 var(--border-radius) var(--border-radius);\n}\n\nmain {\n max-width: 1200px;\n margin: 2rem auto;\n padding: 0 1rem;\n}\n\n.controls {\n background: white;\n padding: 1.5rem;\n border-radius: var(--border-radius);\n box-shadow: var(--box-shadow);\n margin-bottom: 2rem;\n}\n\n.input-group {\n margin-bottom: 1rem;\n}\n\nlabel {\n display: block;\n margin-bottom: 0.5rem;\n font-weight: 500;\n}\n\ninput {\n width: 100%;\n padding: 0.5rem;\n border: 1px solid #ddd;\n border-radius: var(--border-radius);\n}\n\nbutton {\n background-color: var(--primary-color);\n color: white;\n border: none;\n padding: 0.75rem 1.5rem;\n border-radius: var(--border-radius);\n cursor: pointer;\n transition: background-color 0.3s ease;\n}\n\nbutton:hover {\n background-color: darken(var(--primary-color), 10%);\n}\n\n.visualization {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));\n gap: 2rem;\n margin-bottom: 2rem;\n}\n\n.chart-container {\n background: white;\n padding: 1.5rem;\n border-radius: var(--border-radius);\n box-shadow: var(--box-shadow);\n}\n\n.operations {\n text-align: center;\n margin-bottom: 2rem;\n}\n\n.operation-buttons {\n display: flex;\n justify-content: center;\n gap: 1rem;\n flex-wrap: wrap;\n}\n\nfooter {\n text-align: center;\n padding: 1rem;\n background: white;\n margin-top: 2rem;\n border-radius: var(--border-radius) var(--border-radius) 0 0;\n box-shadow: var(--box-shadow);\n}\n\n@media (max-width: 768px) {\n .visualization {\n grid-template-columns: 1fr;\n }\n}\n---script.js---\nconst f1FrequencyInput = document.getElementById('f1-frequency');\nconst f2FrequencyInput = document.getElementById('f2-frequency');\nconst amplitudeInput = document.getElementById('amplitude');\nconst phaseInput = document.getElementById('phase');\nconst generateBtn = document.getElementById('generate-btn');\nconst addBtn = document.getElementById('add-btn');\nconst multiplyBtn = document.getElementById('multiply-btn');\nconst subtractBtn = document.getElementById('subtract-btn');\nconst timeDomainCanvas = document.getElementById('time-domain');\nconst frequencyDomainCanvas = document.getElementById('frequency-domain');\n\nlet f1Signal = [];\nlet f2Signal = [];\nlet combinedSignal = [];\n\nfunction generateSineWave(frequency, amplitude, phase, sampleRate = 44100, duration = 1) {\n const signal = [];\n const numSamples = sampleRate * duration;\n for (let i = 0; i < numSamples; i++) {\n const t = i / sampleRate;\n signal.push(amplitude * Math.sin(2 * Math.PI * frequency * t + phase));\n }\n return signal;\n}\n\nfunction plotTimeDomain(signal, canvas) {\n const ctx = canvas.getContext('2d');\n const width = canvas.width;\n const height = canvas.height;\n ctx.clearRect(0, 0, width, height);\n ctx.beginPath();\n ctx.moveTo(0, height / 2);\n \n const step = width / signal.length;\n for (let i = 0; i < signal.length; i++) {\n const x = i * step;\n const y = height / 2 - signal[i] * (height / 4);\n ctx.lineTo(x, y);\n }\n \n ctx.strokeStyle = '#4a90e2';\n ctx.lineWidth = 2;\n ctx.stroke();\n}\n\nfunction plotFrequencyDomain(signal, canvas) {\n const ctx = canvas.getContext('2d');\n const width = canvas.width;\n const height = canvas.height;\n ctx.clearRect(0, 0, width, height);\n \n const fft = new FFT(signal.length, 44100);\n fft.forward(signal);\n \n const spectrum = fft.spectrum;\n const barWidth = width / spectrum.length;\n \n for (let i = 0; i < spectrum.length; i++) {\n const barHeight = spectrum[i] * height;\n ctx.fillStyle = '#50e3c2';\n ctx.fillRect(i * barWidth, height - barHeight, barWidth, barHeight);\n }\n}\n\nfunction generateSignals() {\n const f1 = parseFloat(f1FrequencyInput.value);\n const f2 = parseFloat(f2FrequencyInput.value);\n const amplitude = parseFloat(amplitudeInput.value);\n const phase = parseFloat(phaseInput.value);\n \n f1Signal = generateSineWave(f1, amplitude, phase);\n f2Signal = generateSineWave(f2, amplitude, phase);\n \n plotTimeDomain(f1Signal, timeDomainCanvas);\n plotFrequencyDomain(f1Signal, frequencyDomainCanvas);\n}\n\nfunction addSignals() {\n combinedSignal = f1Signal.map((value, index) => value + f2Signal[index]);\n plotTimeDomain(combinedSignal, timeDomainCanvas);\n plotFrequencyDomain(combinedSignal, frequencyDomainCanvas);\n}\n\nfunction multiplySignals() {\n combinedSignal = f1Signal.map((value, index) => value * f2Signal[index]);\n plotTimeDomain(combinedSignal, timeDomainCanvas);\n plotFrequencyDomain(combinedSignal, frequencyDomainCanvas);\n}\n\nfunction subtractSignals() {\n combinedSignal = f1Signal.map((value, index) => value - f2Signal[index]);\n plotTimeDomain(combinedSignal, timeDomainCanvas);\n plotFrequencyDomain(combinedSignal, frequencyDomainCanvas);\n}\n\ngenerateBtn.addEventListener('click', generateSignals);\naddBtn.addEventListener('click', addSignals);\nmultiplyBtn.addEventListener('click', multiplySignals);\nsubtractBtn.addEventListener('click', subtractSignals);\n\n// FFT implementation\nclass FFT {\n constructor(bufferSize, sampleRate) {\n this.bufferSize = bufferSize;\n this.sampleRate = sampleRate;\n this.bandwidth = sampleRate / bufferSize;\n this.spectrum = new Float32Array(bufferSize / 2);\n this.real = new Float32Array(bufferSize);\n this.imag = new Float32Array(bufferSize);\n }\n\n forward(buffer) {\n this.real.set(buffer);\n this.imag.fill(0);\n this.transform(this.real, this.imag);\n this.calculateSpectrum();\n }\n\n transform(real, imag) {\n const n = real.length;\n if (n <= 1) return;\n\n const evenReal = new Float32Array(n / 2);\n const evenImag = new Float32Array(n / 2);\n const oddReal = new Float32Array(n / 2);\n const oddImag = new Float32Array(n / 2);\n\n for (let i = 0; i < n / 2; i++) {\n evenReal[i] = real[2 * i];\n evenImag[i] = imag[2 * i];\n oddReal[i] = real[2 * i + 1];\n oddImag[i] = imag[2 * i + 1];\n }\n\n this.transform(evenReal, evenImag);\n this.transform(oddReal, oddImag);\n\n for (let k = 0; k < n / 2; k++) {\n const tReal = Math.cos(2 * Math.PI * k / n) * oddReal[k] - Math.sin(2 * Math.PI * k / n) * oddImag[k];\n const tImag = Math.sin(2 * Math.PI * k / n) * oddReal[k] + Math.cos(2 * Math.PI * k / n) * oddImag[k];\n\n real[k] = evenReal[k] + tReal;\n imag[k] = evenImag[k] + tImag;\n real[k + n / 2] = evenReal[k] - tReal;\n imag[k + n / 2] = evenImag[k] - tImag;\n }\n }\n\n calculateSpectrum() {\n for (let i = 0; i < this.bufferSize / 2; i++) {\n this.spectrum[i] = Math.sqrt(this.real[i] * this.real[i] + this.imag[i] * this.imag[i]);\n }\n }\n}\n---end---"
+ },
+ "logprobs": null,
+ "finish_reason": "stop"
+ }
+ ],
+ "usage": {
+ "prompt_tokens": 283,
+ "completion_tokens": 2568,
+ "total_tokens": 2851,
+ "prompt_cache_hit_tokens": 0,
+ "prompt_cache_miss_tokens": 283
+ },
+ "system_fingerprint": "fp_f1afce2943"
+}
\ No newline at end of file
diff --git a/readme.scroll b/readme.scroll
new file mode 100644
index 0000000..6aaec18
--- /dev/null
+++ b/readme.scroll
@@ -0,0 +1,2 @@
+# signalgen.scroll.pub
+Website generated by DeepSeek from prompt: Stwórz generator sygnałów okresowych f1 oraz f2, oraz ich reprezentację widmową, dodaj odpowiednie parametry i podstawowe operacje na tych sygnałach. Bądź kreatywny.
\ No newline at end of file
diff --git a/script.js b/script.js
new file mode 100644
index 0000000..2b73bf5
--- /dev/null
+++ b/script.js
@@ -0,0 +1,154 @@
+const f1FrequencyInput = document.getElementById('f1-frequency');
+const f2FrequencyInput = document.getElementById('f2-frequency');
+const amplitudeInput = document.getElementById('amplitude');
+const phaseInput = document.getElementById('phase');
+const generateBtn = document.getElementById('generate-btn');
+const addBtn = document.getElementById('add-btn');
+const multiplyBtn = document.getElementById('multiply-btn');
+const subtractBtn = document.getElementById('subtract-btn');
+const timeDomainCanvas = document.getElementById('time-domain');
+const frequencyDomainCanvas = document.getElementById('frequency-domain');
+
+let f1Signal = [];
+let f2Signal = [];
+let combinedSignal = [];
+
+function generateSineWave(frequency, amplitude, phase, sampleRate = 44100, duration = 1) {
+ const signal = [];
+ const numSamples = sampleRate * duration;
+ for (let i = 0; i < numSamples; i++) {
+ const t = i / sampleRate;
+ signal.push(amplitude * Math.sin(2 * Math.PI * frequency * t + phase));
+ }
+ return signal;
+}
+
+function plotTimeDomain(signal, canvas) {
+ const ctx = canvas.getContext('2d');
+ const width = canvas.width;
+ const height = canvas.height;
+ ctx.clearRect(0, 0, width, height);
+ ctx.beginPath();
+ ctx.moveTo(0, height / 2);
+
+ const step = width / signal.length;
+ for (let i = 0; i < signal.length; i++) {
+ const x = i * step;
+ const y = height / 2 - signal[i] * (height / 4);
+ ctx.lineTo(x, y);
+ }
+
+ ctx.strokeStyle = '#4a90e2';
+ ctx.lineWidth = 2;
+ ctx.stroke();
+}
+
+function plotFrequencyDomain(signal, canvas) {
+ const ctx = canvas.getContext('2d');
+ const width = canvas.width;
+ const height = canvas.height;
+ ctx.clearRect(0, 0, width, height);
+
+ const fft = new FFT(signal.length, 44100);
+ fft.forward(signal);
+
+ const spectrum = fft.spectrum;
+ const barWidth = width / spectrum.length;
+
+ for (let i = 0; i < spectrum.length; i++) {
+ const barHeight = spectrum[i] * height;
+ ctx.fillStyle = '#50e3c2';
+ ctx.fillRect(i * barWidth, height - barHeight, barWidth, barHeight);
+ }
+}
+
+function generateSignals() {
+ const f1 = parseFloat(f1FrequencyInput.value);
+ const f2 = parseFloat(f2FrequencyInput.value);
+ const amplitude = parseFloat(amplitudeInput.value);
+ const phase = parseFloat(phaseInput.value);
+
+ f1Signal = generateSineWave(f1, amplitude, phase);
+ f2Signal = generateSineWave(f2, amplitude, phase);
+
+ plotTimeDomain(f1Signal, timeDomainCanvas);
+ plotFrequencyDomain(f1Signal, frequencyDomainCanvas);
+}
+
+function addSignals() {
+ combinedSignal = f1Signal.map((value, index) => value + f2Signal[index]);
+ plotTimeDomain(combinedSignal, timeDomainCanvas);
+ plotFrequencyDomain(combinedSignal, frequencyDomainCanvas);
+}
+
+function multiplySignals() {
+ combinedSignal = f1Signal.map((value, index) => value * f2Signal[index]);
+ plotTimeDomain(combinedSignal, timeDomainCanvas);
+ plotFrequencyDomain(combinedSignal, frequencyDomainCanvas);
+}
+
+function subtractSignals() {
+ combinedSignal = f1Signal.map((value, index) => value - f2Signal[index]);
+ plotTimeDomain(combinedSignal, timeDomainCanvas);
+ plotFrequencyDomain(combinedSignal, frequencyDomainCanvas);
+}
+
+generateBtn.addEventListener('click', generateSignals);
+addBtn.addEventListener('click', addSignals);
+multiplyBtn.addEventListener('click', multiplySignals);
+subtractBtn.addEventListener('click', subtractSignals);
+
+// FFT implementation
+class FFT {
+ constructor(bufferSize, sampleRate) {
+ this.bufferSize = bufferSize;
+ this.sampleRate = sampleRate;
+ this.bandwidth = sampleRate / bufferSize;
+ this.spectrum = new Float32Array(bufferSize / 2);
+ this.real = new Float32Array(bufferSize);
+ this.imag = new Float32Array(bufferSize);
+ }
+
+ forward(buffer) {
+ this.real.set(buffer);
+ this.imag.fill(0);
+ this.transform(this.real, this.imag);
+ this.calculateSpectrum();
+ }
+
+ transform(real, imag) {
+ const n = real.length;
+ if (n <= 1) return;
+
+ const evenReal = new Float32Array(n / 2);
+ const evenImag = new Float32Array(n / 2);
+ const oddReal = new Float32Array(n / 2);
+ const oddImag = new Float32Array(n / 2);
+
+ for (let i = 0; i < n / 2; i++) {
+ evenReal[i] = real[2 * i];
+ evenImag[i] = imag[2 * i];
+ oddReal[i] = real[2 * i + 1];
+ oddImag[i] = imag[2 * i + 1];
+ }
+
+ this.transform(evenReal, evenImag);
+ this.transform(oddReal, oddImag);
+
+ for (let k = 0; k < n / 2; k++) {
+ const tReal = Math.cos(2 * Math.PI * k / n) * oddReal[k] - Math.sin(2 * Math.PI * k / n) * oddImag[k];
+ const tImag = Math.sin(2 * Math.PI * k / n) * oddReal[k] + Math.cos(2 * Math.PI * k / n) * oddImag[k];
+
+ real[k] = evenReal[k] + tReal;
+ imag[k] = evenImag[k] + tImag;
+ real[k + n / 2] = evenReal[k] - tReal;
+ imag[k + n / 2] = evenImag[k] - tImag;
+ }
+ }
+
+ calculateSpectrum() {
+ for (let i = 0; i < this.bufferSize / 2; i++) {
+ this.spectrum[i] = Math.sqrt(this.real[i] * this.real[i] + this.imag[i] * this.imag[i]);
+ }
+ }
+}
\ No newline at end of file
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..eb2fe48
--- /dev/null
+++ b/style.css
@@ -0,0 +1,111 @@
+:root {
+ --primary-color: #4a90e2;
+ --secondary-color: #50e3c2;
+ --background-color: #f5f7fa;
+ --text-color: #333;
+ --border-radius: 8px;
+ --box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+}
+
+body {
+ font-family: 'Segoe UI', sans-serif;
+ line-height: 1.6;
+ margin: 0;
+ padding: 0;
+ background-color: var(--background-color);
+ color: var(--text-color);
+}
+
+header {
+ background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
+ color: white;
+ padding: 2rem;
+ text-align: center;
+ border-radius: 0 0 var(--border-radius) var(--border-radius);
+}
+
+main {
+ max-width: 1200px;
+ margin: 2rem auto;
+ padding: 0 1rem;
+}
+
+.controls {
+ background: white;
+ padding: 1.5rem;
+ border-radius: var(--border-radius);
+ box-shadow: var(--box-shadow);
+ margin-bottom: 2rem;
+}
+
+.input-group {
+ margin-bottom: 1rem;
+}
+
+label {
+ display: block;
+ margin-bottom: 0.5rem;
+ font-weight: 500;
+}
+
+input {
+ width: 100%;
+ padding: 0.5rem;
+ border: 1px solid #ddd;
+ border-radius: var(--border-radius);
+}
+
+button {
+ background-color: var(--primary-color);
+ color: white;
+ border: none;
+ padding: 0.75rem 1.5rem;
+ border-radius: var(--border-radius);
+ cursor: pointer;
+ transition: background-color 0.3s ease;
+}
+
+button:hover {
+ background-color: darken(var(--primary-color), 10%);
+}
+
+.visualization {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+ gap: 2rem;
+ margin-bottom: 2rem;
+}
+
+.chart-container {
+ background: white;
+ padding: 1.5rem;
+ border-radius: var(--border-radius);
+ box-shadow: var(--box-shadow);
+}
+
+.operations {
+ text-align: center;
+ margin-bottom: 2rem;
+}
+
+.operation-buttons {
+ display: flex;
+ justify-content: center;
+ gap: 1rem;
+ flex-wrap: wrap;
+}
+
+footer {
+ text-align: center;
+ padding: 1rem;
+ background: white;
+ margin-top: 2rem;
+ border-radius: var(--border-radius) var(--border-radius) 0 0;
+ box-shadow: var(--box-shadow);
+}
+
+@media (max-width: 768px) {
+ .visualization {
+ grid-template-columns: 1fr;
+ }
+}
\ No newline at end of file