diff --git a/plots/sequence-logo-basic/implementations/plotly.py b/plots/sequence-logo-basic/implementations/plotly.py
new file mode 100644
index 0000000000..02eb8a1d9a
--- /dev/null
+++ b/plots/sequence-logo-basic/implementations/plotly.py
@@ -0,0 +1,241 @@
+""" pyplots.ai
+sequence-logo-basic: Sequence Logo for Motif Visualization
+Library: plotly 6.6.0 | Python 3.14.3
+Quality: 86/100 | Created: 2026-03-06
+"""
+
+import numpy as np
+import plotly.graph_objects as go
+
+
+# Data - transcription factor binding site motif (10-position DNA)
+# Position weight matrix: each row is [A, C, G, T] frequencies
+pwm = np.array(
+ [
+ [0.05, 0.80, 0.05, 0.10], # pos 1: C dominant
+ [0.10, 0.15, 0.10, 0.65], # pos 2: T dominant
+ [0.02, 0.96, 0.01, 0.01], # pos 3: C highly conserved (~1.8 bits)
+ [0.25, 0.25, 0.25, 0.25], # pos 4: uniform (0 bits)
+ [0.70, 0.05, 0.15, 0.10], # pos 5: A dominant
+ [0.10, 0.10, 0.70, 0.10], # pos 6: G dominant
+ [0.001, 0.001, 0.001, 0.997], # pos 7: T near-perfect conservation (~1.97 bits)
+ [0.60, 0.15, 0.15, 0.10], # pos 8: A dominant
+ [0.10, 0.10, 0.65, 0.15], # pos 9: G dominant
+ [0.15, 0.55, 0.10, 0.20], # pos 10: C dominant
+ ]
+)
+
+letters = ["A", "C", "G", "T"]
+# Standard DNA sequence logo colors: A=green, C=blue, G=orange, T=red
+# Using colorblind-safe Wong palette variants
+colors = {"A": "#009E73", "C": "#0072B2", "G": "#E69F00", "T": "#D55E00"}
+n_positions = len(pwm)
+
+# Information content: IC = 2 + sum(f * log2(f)) for DNA (max 2 bits)
+info_content = np.zeros(n_positions)
+for i in range(n_positions):
+ entropy = sum(f * np.log2(f) for f in pwm[i] if f > 0)
+ info_content[i] = max(0, 2.0 + entropy)
+
+# Letter heights = frequency * information content at each position
+letter_heights = pwm * info_content[:, np.newaxis]
+
+# SVG path data for letters (simplified block-style glyphs within 0-1 x 0-1 box)
+letter_paths = {
+ "A": "M 0.5 0 L 0.05 1 L 0.25 1 L 0.35 0.7 L 0.65 0.7 L 0.75 1 L 0.95 1 L 0.5 0 Z M 0.4 0.52 L 0.6 0.52 L 0.55 0.38 L 0.45 0.38 Z",
+ "C": "M 0.85 0.2 C 0.65 -0.05 0.2 0 0.1 0.3 C 0 0.6 0.15 0.95 0.5 1 C 0.7 1.02 0.85 0.9 0.88 0.8 L 0.68 0.7 C 0.6 0.82 0.5 0.82 0.4 0.78 C 0.28 0.7 0.25 0.5 0.3 0.35 C 0.35 0.2 0.5 0.15 0.6 0.18 C 0.68 0.2 0.72 0.28 0.75 0.32 Z",
+ "G": "M 0.85 0.2 C 0.65 -0.05 0.2 0 0.1 0.3 C 0 0.6 0.15 0.95 0.5 1 C 0.7 1.02 0.85 0.9 0.88 0.8 L 0.68 0.7 C 0.6 0.82 0.5 0.82 0.4 0.78 C 0.28 0.7 0.25 0.5 0.3 0.35 C 0.35 0.2 0.5 0.15 0.6 0.18 C 0.68 0.2 0.72 0.28 0.75 0.32 L 0.85 0.2 Z M 0.55 0.45 L 0.85 0.45 L 0.85 0.55 L 0.55 0.55 Z",
+ "T": "M 0.05 0 L 0.05 0.18 L 0.38 0.18 L 0.38 1 L 0.62 1 L 0.62 0.18 L 0.95 0.18 L 0.95 0 Z",
+}
+
+# Plot - using Plotly shapes for stretched letter glyphs
+fig = go.Figure()
+bar_width = 0.38 # half-width for shapes
+
+# Build letter shapes at each position
+for pos in range(n_positions):
+ heights = letter_heights[pos]
+ sorted_indices = np.argsort(heights)
+ y_bottom = 0
+
+ for idx in sorted_indices:
+ h = heights[idx]
+ if h < 0.005:
+ continue
+ letter = letters[idx]
+
+ # Add invisible bar for hover interaction
+ fig.add_trace(
+ go.Bar(
+ x=[pos + 1],
+ y=[h],
+ base=y_bottom,
+ width=bar_width * 2,
+ marker={"color": "rgba(0,0,0,0)", "line": {"width": 0}},
+ showlegend=False,
+ hovertemplate=(
+ f"Position {pos + 1}
"
+ f"Nucleotide: {letter}
"
+ f"Frequency: {pwm[pos][idx]:.0%}
"
+ f"Height: {h:.3f} bits"
+ f""
+ ),
+ )
+ )
+
+ # Transform SVG path from 0-1 space to data coordinates (inline)
+ tokens = letter_paths[letter].split()
+ path_parts = []
+ ti = 0
+ while ti < len(tokens):
+ cmd = tokens[ti]
+ if cmd in ("M", "L", "Z"):
+ path_parts.append(cmd)
+ if cmd != "Z":
+ px = float(tokens[ti + 1])
+ py = float(tokens[ti + 2])
+ path_parts.append(str((pos + 1) - bar_width + px * 2 * bar_width))
+ path_parts.append(str(y_bottom + py * h))
+ ti += 3
+ else:
+ ti += 1
+ elif cmd == "C":
+ path_parts.append(cmd)
+ for j in range(3):
+ px = float(tokens[ti + 1 + j * 2])
+ py = float(tokens[ti + 2 + j * 2])
+ path_parts.append(str((pos + 1) - bar_width + px * 2 * bar_width))
+ path_parts.append(str(y_bottom + py * h))
+ ti += 7
+ else:
+ ti += 1
+ path_data = " ".join(path_parts)
+
+ fig.add_shape(
+ type="path",
+ path=path_data,
+ fillcolor=colors[letter],
+ line={"width": 0.5, "color": colors[letter]},
+ layer="above",
+ xref="x",
+ yref="y",
+ )
+
+ y_bottom += h
+
+# Legend entries
+for letter in letters:
+ fig.add_trace(
+ go.Scatter(
+ x=[None],
+ y=[None],
+ mode="markers",
+ marker={"size": 18, "color": colors[letter], "symbol": "square"},
+ name=f" {letter} ",
+ showlegend=True,
+ )
+ )
+
+# Style
+fig.update_layout(
+ title={
+ "text": "sequence-logo-basic · plotly · pyplots.ai",
+ "font": {"size": 28, "family": "Arial, Helvetica, sans-serif", "color": "#1a1a2e"},
+ "x": 0.5,
+ "y": 0.96,
+ },
+ xaxis={
+ "title": {
+ "text": "Position",
+ "font": {"size": 22, "color": "#1a1a2e", "family": "Arial, sans-serif"},
+ "standoff": 12,
+ },
+ "tickfont": {"size": 18, "color": "#4a4a68", "family": "Arial, sans-serif"},
+ "tickvals": list(range(1, n_positions + 1)),
+ "showline": True,
+ "linewidth": 2,
+ "linecolor": "#1a1a2e",
+ "mirror": False,
+ "showgrid": False,
+ "zeroline": False,
+ "ticks": "outside",
+ "ticklen": 8,
+ "tickwidth": 1.5,
+ "tickcolor": "#4a4a68",
+ },
+ yaxis={
+ "title": {
+ "text": "Information content (bits)",
+ "font": {"size": 22, "color": "#1a1a2e", "family": "Arial, sans-serif"},
+ "standoff": 10,
+ },
+ "tickfont": {"size": 18, "color": "#4a4a68", "family": "Arial, sans-serif"},
+ "range": [0, 2.15],
+ "showline": True,
+ "linewidth": 2,
+ "linecolor": "#1a1a2e",
+ "mirror": False,
+ "gridwidth": 0.5,
+ "gridcolor": "rgba(100,100,140,0.08)",
+ "griddash": "dot",
+ "zeroline": True,
+ "zerolinewidth": 2,
+ "zerolinecolor": "#1a1a2e",
+ "ticks": "outside",
+ "ticklen": 8,
+ "tickwidth": 1.5,
+ "tickcolor": "#4a4a68",
+ "dtick": 0.5,
+ },
+ template="plotly_white",
+ barmode="overlay",
+ bargap=0,
+ plot_bgcolor="white",
+ paper_bgcolor="white",
+ legend={
+ "font": {"size": 20, "family": "Arial Black, Impact, sans-serif"},
+ "orientation": "h",
+ "yanchor": "bottom",
+ "y": 1.04,
+ "xanchor": "center",
+ "x": 0.5,
+ "bgcolor": "rgba(0,0,0,0)",
+ "tracegroupgap": 20,
+ },
+ margin={"l": 90, "r": 50, "t": 120, "b": 70},
+ hoverlabel={"bgcolor": "white", "bordercolor": "#4a4a68", "font": {"size": 15, "family": "Arial, sans-serif"}},
+)
+
+# Annotate highly conserved positions with larger, more prominent labels
+conserved_positions = [2, 6] # positions 3 and 7 (0-indexed)
+for pos_idx in conserved_positions:
+ ic_val = info_content[pos_idx]
+ fig.add_annotation(
+ x=pos_idx + 1,
+ y=ic_val + 0.08,
+ text=f"▼ {ic_val:.2f} bits",
+ font={
+ "size": 16,
+ "color": "#1a1a2e",
+ "family": "Arial, sans-serif",
+ "weight": "bold" if ic_val > 1.9 else "normal",
+ },
+ showarrow=False,
+ yanchor="bottom",
+ xanchor="center",
+ )
+
+# Add subtle annotation for the zero-information position
+fig.add_annotation(
+ x=4,
+ y=-0.08,
+ text="no signal",
+ font={"size": 13, "color": "#999999", "family": "Arial, sans-serif"},
+ showarrow=False,
+ yanchor="top",
+ xanchor="center",
+)
+
+# Save
+fig.write_html("plot.html")
+fig.write_image("plot.png", width=1600, height=900, scale=3)
diff --git a/plots/sequence-logo-basic/metadata/plotly.yaml b/plots/sequence-logo-basic/metadata/plotly.yaml
new file mode 100644
index 0000000000..32e72475b2
--- /dev/null
+++ b/plots/sequence-logo-basic/metadata/plotly.yaml
@@ -0,0 +1,232 @@
+library: plotly
+specification_id: sequence-logo-basic
+created: '2026-03-06T20:26:49Z'
+updated: '2026-03-06T20:58:43Z'
+generated_by: claude-opus-4-5-20251101
+workflow_run: 22780525003
+issue: 4421
+python_version: 3.14.3
+library_version: 6.6.0
+preview_url: https://storage.googleapis.com/pyplots-images/plots/sequence-logo-basic/plotly/plot.png
+preview_thumb: https://storage.googleapis.com/pyplots-images/plots/sequence-logo-basic/plotly/plot_thumb.png
+preview_html: https://storage.googleapis.com/pyplots-images/plots/sequence-logo-basic/plotly/plot.html
+quality_score: 86
+review:
+ strengths:
+ - Creative SVG path shapes render stretched letter glyphs matching spec requirement
+ for scaled glyphs
+ - Excellent data quality with biologically plausible PWM covering full conservation
+ range
+ - Strong design with Wong palette, annotations on conserved positions, and clean
+ visual refinement
+ - Interactive hover tooltips provide nucleotide frequency details as distinctive
+ Plotly feature
+ weaknesses:
+ - SVG path transformation code is complex and verbose, reducing code readability
+ - Small letters at low-IC positions (9-10) are crowded and hard to distinguish
+ - Position 7 T glyph appears somewhat rectangular rather than clearly T-shaped at
+ tall heights
+ image_description: 'The plot shows a DNA sequence logo for a 10-position transcription
+ factor binding site motif. Letters (A, C, G, T) are rendered as custom SVG-path
+ glyphs stacked vertically at each position, with heights proportional to information
+ content in bits. Position 3 has a tall blue C (~1.70 bits) and position 7 has
+ a near-maximum-height orange/red T (~1.97 bits), both annotated with downward
+ triangles and bit values. Position 4 is empty (zero information) with a "no signal"
+ label below. The remaining positions show varying conservation levels with stacked
+ letters of different sizes. Colors follow standard DNA conventions using Wong
+ palette variants: A=green, C=blue, G=orange, T=red-orange. The title reads "sequence-logo-basic
+ · plotly · pyplots.ai" centered at top with a horizontal legend (A, C, G, T squares)
+ below it. X-axis labeled "Position" (1-10), Y-axis labeled "Information content
+ (bits)" (0-2.0). Clean white background with subtle dotted y-grid lines.'
+ criteria_checklist:
+ visual_quality:
+ score: 27
+ max: 30
+ items:
+ - id: VQ-01
+ name: Text Legibility
+ score: 8
+ max: 8
+ passed: true
+ comment: 'All font sizes explicitly set: title 28, axis labels 22, ticks 18,
+ annotations 16/13'
+ - id: VQ-02
+ name: No Overlap
+ score: 5
+ max: 6
+ passed: true
+ comment: Minor crowding at low-IC positions 9-10 where small glyphs overlap
+ slightly
+ - id: VQ-03
+ name: Element Visibility
+ score: 5
+ max: 6
+ passed: true
+ comment: High-IC letters excellent; low-IC positions have very small letters
+ hard to distinguish
+ - id: VQ-04
+ name: Color Accessibility
+ score: 4
+ max: 4
+ passed: true
+ comment: Wong palette variants are colorblind-safe with good contrast
+ - id: VQ-05
+ name: Layout & Canvas
+ score: 3
+ max: 4
+ passed: true
+ comment: Good proportions overall; some vertical wasted space in middle area
+ - id: VQ-06
+ name: Axis Labels & Title
+ score: 2
+ max: 2
+ passed: true
+ comment: Information content (bits) and Position - descriptive with units
+ design_excellence:
+ score: 16
+ max: 20
+ items:
+ - id: DE-01
+ name: Aesthetic Sophistication
+ score: 6
+ max: 8
+ passed: true
+ comment: Strong design with custom SVG glyphs, Wong palette, intentional color
+ scheme, plotly_white template
+ - id: DE-02
+ name: Visual Refinement
+ score: 5
+ max: 6
+ passed: true
+ comment: Spines only on bottom/left, subtle dotted y-grid, x-grid removed,
+ generous whitespace
+ - id: DE-03
+ name: Data Storytelling
+ score: 5
+ max: 6
+ passed: true
+ comment: Annotations highlight conserved positions, no signal label creates
+ contrast, visual hierarchy through heights
+ spec_compliance:
+ score: 14
+ max: 15
+ items:
+ - id: SC-01
+ name: Plot Type
+ score: 5
+ max: 5
+ passed: true
+ comment: Correct sequence logo with stacked letters proportional to information
+ content
+ - id: SC-02
+ name: Required Features
+ score: 3
+ max: 4
+ passed: true
+ comment: All major features present; T glyph at position 7 appears somewhat
+ rectangular
+ - id: SC-03
+ name: Data Mapping
+ score: 3
+ max: 3
+ passed: true
+ comment: X-axis positions 1-10, Y-axis IC in bits 0-2.15, correct mapping
+ - id: SC-04
+ name: Title & Legend
+ score: 3
+ max: 3
+ passed: true
+ comment: Title format correct, legend shows A C G T with matching colors
+ data_quality:
+ score: 15
+ max: 15
+ items:
+ - id: DQ-01
+ name: Feature Coverage
+ score: 6
+ max: 6
+ passed: true
+ comment: 'Excellent variety: highly conserved, moderate, low conservation,
+ and zero information positions'
+ - id: DQ-02
+ name: Realistic Context
+ score: 5
+ max: 5
+ passed: true
+ comment: Transcription factor binding site motif with biologically plausible
+ PWM values
+ - id: DQ-03
+ name: Appropriate Scale
+ score: 4
+ max: 4
+ passed: true
+ comment: IC values correct for DNA (max ~2 bits), frequencies sum to 1.0
+ code_quality:
+ score: 8
+ max: 10
+ items:
+ - id: CQ-01
+ name: KISS Structure
+ score: 2
+ max: 3
+ passed: true
+ comment: Follows imports-data-plot-save but SVG path transformation adds inline
+ complexity
+ - id: CQ-02
+ name: Reproducibility
+ score: 2
+ max: 2
+ passed: true
+ comment: Fully deterministic hardcoded PWM data
+ - id: CQ-03
+ name: Clean Imports
+ score: 2
+ max: 2
+ passed: true
+ comment: Only numpy and plotly.graph_objects, both used
+ - id: CQ-04
+ name: Code Elegance
+ score: 1
+ max: 2
+ passed: true
+ comment: SVG path parsing/transformation is clever but verbose and hard to
+ follow
+ - id: CQ-05
+ name: Output & API
+ score: 1
+ max: 1
+ passed: true
+ comment: Saves as plot.png at 4800x2700, also HTML, current API
+ library_mastery:
+ score: 6
+ max: 10
+ items:
+ - id: LM-01
+ name: Idiomatic Usage
+ score: 3
+ max: 5
+ passed: true
+ comment: Uses go.Figure, go.Bar, go.Scatter, add_shape correctly but heavy
+ SVG path manipulation is a workaround
+ - id: LM-02
+ name: Distinctive Features
+ score: 3
+ max: 5
+ passed: true
+ comment: Leverages Plotly SVG shape system, interactive hover via invisible
+ bars, HTML export
+ verdict: REJECTED
+impl_tags:
+ dependencies: []
+ techniques:
+ - annotations
+ - custom-legend
+ - hover-tooltips
+ - html-export
+ patterns:
+ - data-generation
+ - iteration-over-groups
+ dataprep: []
+ styling:
+ - grid-styling
+ - edge-highlighting