{"spec_id":"spectrum-basic","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\nspectrum-basic: Frequency Spectrum Plot\nLibrary: pygal 3.1.0 | Python 3.13.13\nQuality: 88/100 | Updated: 2026-05-14\n\"\"\"\n\nimport os\nimport sys\n\nimport numpy as np\n\n\nsys.path.pop(0)\nimport pygal\nfrom pygal.style import Style\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\nBRAND = \"#009E73\"  # Okabe-Ito position 1\n\n# Data - Generate synthetic signal with multiple frequency components\nnp.random.seed(42)\n\n# Create a synthetic time-domain signal with known frequency components\nfs = 1000  # Sampling frequency (Hz)\nt = np.linspace(0, 1, fs)  # 1 second of data\n\n# Signal with 3 frequency components: 50 Hz (strong), 120 Hz (medium), 200 Hz (weak)\nsignal = (\n    3.0 * np.sin(2 * np.pi * 50 * t)  # 50 Hz - dominant frequency\n    + 1.5 * np.sin(2 * np.pi * 120 * t)  # 120 Hz - secondary\n    + 0.8 * np.sin(2 * np.pi * 200 * t)  # 200 Hz - tertiary\n    + 0.3 * np.random.randn(len(t))  # Noise floor\n)\n\n# Compute FFT\nn = len(signal)\nfft_result = np.fft.fft(signal)\nfrequencies = np.fft.fftfreq(n, 1 / fs)\n\n# Take only positive frequencies\npositive_mask = frequencies >= 0\nfrequencies = frequencies[positive_mask]\namplitude = np.abs(fft_result[positive_mask]) / n * 2  # Normalize\n\n# Convert to dB scale for better visualization\namplitude_db = 20 * np.log10(amplitude + 1e-10)\n\n# Limit to 0-300 Hz for clarity (where our signal components are)\nfreq_limit_mask = frequencies <= 300\nfrequencies = frequencies[freq_limit_mask]\namplitude_db = amplitude_db[freq_limit_mask]\n\n# Downsample for pygal (it works better with fewer points)\nstep = 2\nfrequencies = frequencies[::step]\namplitude_db = amplitude_db[::step]\n\n# Create custom style for large canvas with theme-adaptive colors\ncustom_style = Style(\n    background=PAGE_BG,\n    plot_background=PAGE_BG,\n    foreground=INK,\n    foreground_strong=INK,\n    foreground_subtle=INK_MUTED,\n    colors=(BRAND,),\n    title_font_size=28,\n    label_font_size=22,\n    major_label_font_size=18,\n    legend_font_size=16,\n    value_font_size=14,\n    stroke_width=3,\n    opacity=0.85,\n    opacity_hover=1.0,\n)\n\n# Create XY chart (line chart with custom x values)\nchart = pygal.XY(\n    width=4800,\n    height=2700,\n    style=custom_style,\n    title=\"spectrum-basic · pygal · anyplot.ai\",\n    x_title=\"Frequency (Hz)\",\n    y_title=\"Amplitude (dB)\",\n    show_dots=False,\n    fill=True,\n    stroke_style={\"width\": 3},\n    show_x_guides=True,\n    show_y_guides=True,\n    x_label_rotation=0,\n    show_legend=False,\n    range=(-60, 20),  # dB range\n    dots_size=0,\n    margin=80,\n    spacing=40,\n)\n\n# Add data as XY points\nxy_data = [(float(f), float(a)) for f, a in zip(frequencies, amplitude_db, strict=True)]\nchart.add(\"Amplitude\", xy_data)\n\n# Save as PNG and HTML with theme suffix\nchart.render_to_png(f\"plot-{THEME}.png\")\nwith open(f\"plot-{THEME}.html\", \"wb\") as f:\n    f.write(chart.render())\n"}