{"spec_id":"spectrogram-basic","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nspectrogram-basic: Spectrogram Time-Frequency Heatmap\nLibrary: plotly 6.7.0 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-05-15\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport plotly.graph_objects as go\nfrom scipy import signal\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_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nGRID = \"rgba(26,26,23,0.10)\" if THEME == \"light\" else \"rgba(240,239,232,0.10)\"\n\n# Data - EEG biomedical signal with alpha-beta oscillations\nnp.random.seed(42)\nsample_rate = 256  # Hz (standard EEG sampling rate)\nduration = 4.0  # seconds\nt = np.linspace(0, duration, int(sample_rate * duration))\n\n# Simulate EEG with multiple frequency components\n# Alpha waves (8-12 Hz), beta waves (12-30 Hz), and muscle artifact (50 Hz)\nalpha = 2.0 * np.sin(2 * np.pi * 10 * t)  # 10 Hz alpha waves\nbeta = 1.5 * np.sin(2 * np.pi * 20 * t + np.pi / 4)  # 20 Hz beta waves\nmuscle_artifact = 0.8 * np.sin(2 * np.pi * 50 * t)  # 50 Hz artifact\n\n# Add realistic noise and transient bursts\nnoise = np.random.randn(len(t)) * 0.5\ntransient = np.zeros_like(t)\ntransient[int(1.5 * sample_rate) : int(2.2 * sample_rate)] += 3.0 * np.sin(\n    2 * np.pi * 15 * t[int(1.5 * sample_rate) : int(2.2 * sample_rate)]\n)\n\neeg_signal = alpha + beta + muscle_artifact + noise + transient\n\n# Compute spectrogram\nnperseg = 128  # Window size for better frequency resolution\nnoverlap = 96  # Overlap (75% overlap for smooth visualization)\nfrequencies, times, Sxx = signal.spectrogram(eeg_signal, fs=sample_rate, nperseg=nperseg, noverlap=noverlap)\n\n# Limit frequency range to 0-60 Hz (typical EEG band of interest)\nfreq_mask = frequencies <= 60\nfrequencies = frequencies[freq_mask]\nSxx = Sxx[freq_mask, :]\n\n# Convert to dB scale for better visualization\nSxx_db = 10 * np.log10(Sxx + 1e-10)\n\n# Create spectrogram heatmap\nfig = go.Figure()\n\nfig.add_trace(\n    go.Heatmap(\n        x=times,\n        y=frequencies,\n        z=Sxx_db,\n        colorscale=\"Viridis\",\n        colorbar={\n            \"title\": {\"text\": \"Power (dB)\", \"font\": {\"size\": 20, \"color\": INK}},\n            \"tickfont\": {\"size\": 16, \"color\": INK_SOFT},\n            \"len\": 0.85,\n            \"thickness\": 25,\n            \"outlinecolor\": INK_SOFT,\n            \"bordercolor\": INK_SOFT,\n            \"borderwidth\": 1,\n        },\n        hovertemplate=\"Time: %{x:.2f}s<br>Frequency: %{y:.1f}Hz<br>Power: %{z:.1f}dB<extra></extra>\",\n    )\n)\n\n# Layout\nfig.update_layout(\n    title={\n        \"text\": \"spectrogram-basic · plotly · anyplot.ai\",\n        \"font\": {\"size\": 28, \"color\": INK},\n        \"x\": 0.5,\n        \"xanchor\": \"center\",\n    },\n    xaxis={\n        \"title\": {\"text\": \"Time (seconds)\", \"font\": {\"size\": 22, \"color\": INK}},\n        \"tickfont\": {\"size\": 18, \"color\": INK_SOFT},\n        \"gridcolor\": GRID,\n        \"linecolor\": INK_SOFT,\n        \"showgrid\": False,\n    },\n    yaxis={\n        \"title\": {\"text\": \"Frequency (Hz)\", \"font\": {\"size\": 22, \"color\": INK}},\n        \"tickfont\": {\"size\": 18, \"color\": INK_SOFT},\n        \"gridcolor\": GRID,\n        \"linecolor\": INK_SOFT,\n        \"showgrid\": False,\n    },\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    font={\"color\": INK},\n    margin={\"l\": 100, \"r\": 120, \"t\": 100, \"b\": 100},\n    height=None,\n    width=None,\n)\n\n# Save outputs\nfig.write_image(f\"plot-{THEME}.png\", width=1600, height=900, scale=3)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}