Generate interest-themed SVG concept diagrams for the math tutoring platform (grades 6-12). Use this skill whenever you need to create static visual assets — concept diagrams, scene setups, solution steps, number lines, or area models — for personalized math content. Trigger on any request involving SVG generation, math diagrams, visual assets for content, or "generate images for" a concept/interest/grade combination. Claude Code generates SVG code directly (you ARE the LLM) and writes via TutorDataServiceWriter.
You generate interest-themed SVG diagrams for math concepts. Each diagram teaches something mathematical while using the student's interest (basketball, gaming, space, etc.) as a visual wrapper.
You ARE the LLM — generate SVG code directly. Do NOT call any external generation APIs.
import sys
from pathlib import Path
sys.path.insert(0, str(Path("src")))
from math_content_engine.integration.tutor_writer import TutorDataServiceWriter
from math_content_engine.personalization.theme_mapper import interest_to_theme, PIPELINE_INTERESTS
writer = TutorDataServiceWriter()
viewBox="0 0 400 300" with xmlns="http://www.w3.org/2000/svg". Responsive via width="100%" height="auto".#0f0f23 (space dark). Always fill the entire viewBox.#60a5fa (blue), (green), (pink), (gold), (text), (emphasis). Use interest accent color as primary.#4ade80#ec4899#ffd700#e2e8f0#fffffffont-family="system-ui, sans-serif". Titles 16-18px bold, labels 12-14px, small text 10px.rx="6"), subtle glows (filter with feGaussianBlur), high contrast. Gaming UI / Discord aesthetic.| Type | Use For | Example |
|---|---|---|
concept_diagram | Visual explanation of a math concept | Coordinate plane with labeled line, area model for factoring |
scene_setup | Themed scene framing a problem | Basketball court with measurements, game HUD with stats |
solution_step | One key step in a solution | Equation transformation with color-coded operations |
number_line | Number line for inequalities, comparisons | Inequality solution highlighted on a number line |
area_model | Area/box model for multiplication, factoring | Rectangular area model showing distribution |
| Interest | Accent | Secondary | Frame Decoration |
|---|---|---|---|
| basketball | #f97316 | #ea580c | Scoreboard header, court lines |
| soccer | #4ade80 | #16a34a | Pitch lines, net pattern |
| football | #22c55e | #16a34a | Yard line markers, scoreboard digits |
| gaming | #8b5cf6 | #7c3aed | HUD borders, pixel corners, HP/XP bars |
| pokemon | #fbbf24 | #f59e0b | Pokédex border, type badge |
| space | #3b82f6 | #2563eb | Mission control panel, star field dots |
| animals | #4ade80 | #22c55e | Paw print accents, habitat border |
| music | #a855f7 | #9333ea | Equalizer bars, waveform border |
| cooking | #f59e0b | #d97706 | Recipe card layout, measurement marks |
| art | #ec4899 | #db2777 | Canvas frame, paint palette dots |
| robots | #3b82f6 | #2563eb | Circuit board traces, LED dots |
Each image is a dict passed to writer.write_personalized_content(images=[...]):
{
"asset_id": "ch1_pipeline_img_1_2_basketball_grade_8_01",
"type": "concept_diagram", # concept_diagram | scene_setup | solution_step | number_line | area_model
"title": "Linear Functions on the Court",
"format": "svg",
"data": '<svg viewBox="0 0 400 300" xmlns="http://www.w3.org/2000/svg">...</svg>',
"width": 400,
"height": 300,
"display_order": 0,
}
ID convention: {source}_img_{section}_{interest}_{grade}_{seq:02d}
Text labels must NEVER overlap with graph lines, axes, or other labels:
fill="#0f0f23" opacity="0.85") behind the text. Position the label AWAY from the line it describes — offset by at least 15px perpendicular to the line direction.y offset of +20px below x-axis ticks, or x offset of -30px left of y-axis ticks.Before including any SVG, verify:
#0f0f23 (full viewBox fill)system-ui font, not cursive/handwritingviewBox="0 0 400 300" presentConcept: Linear Functions (y = mx + b) | Interest: Basketball | Grade: 8
<svg viewBox="0 0 400 300" xmlns="http://www.w3.org/2000/svg">
<!-- Background -->
<rect width="400" height="300" fill="#0f0f23"/>
<!-- Scoreboard Header (basketball theme) -->
<rect x="10" y="8" width="380" height="32" rx="6" fill="#1a1a2e" stroke="#f97316" stroke-width="1.5"/>
<circle cx="30" cy="24" r="8" fill="none" stroke="#f97316" stroke-width="1.5"/>
<line x1="30" y1="16" x2="30" y2="32" stroke="#f97316" stroke-width="0.5" opacity="0.5"/>
<text x="200" y="29" text-anchor="middle" fill="#f97316" font-family="system-ui, sans-serif" font-size="14" font-weight="bold">Points Per Game: y = 3x + 5</text>
<!-- Glow filter -->
<defs>
<filter id="glow">
<feGaussianBlur stdDeviation="2" result="blur"/>
<feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
</defs>
<!-- Coordinate Plane -->
<!-- Grid lines -->
<g stroke="#e2e8f0" stroke-width="0.3" opacity="0.15">
<line x1="80" y1="60" x2="80" y2="270"/><line x1="140" y1="60" x2="140" y2="270"/>
<line x1="200" y1="60" x2="200" y2="270"/><line x1="260" y1="60" x2="260" y2="270"/>
<line x1="320" y1="60" x2="320" y2="270"/>
<line x1="50" y1="90" x2="370" y2="90"/><line x1="50" y1="130" x2="370" y2="130"/>
<line x1="50" y1="170" x2="370" y2="170"/><line x1="50" y1="210" x2="370" y2="210"/>
<line x1="50" y1="250" x2="370" y2="250"/>
</g>
<!-- Axes -->
<line x1="50" y1="270" x2="370" y2="270" stroke="#e2e8f0" stroke-width="1.5"/>
<line x1="50" y1="60" x2="50" y2="270" stroke="#e2e8f0" stroke-width="1.5"/>
<!-- X-axis labels (Games) -->
<text x="80" y="286" text-anchor="middle" fill="#94a3b8" font-family="system-ui, sans-serif" font-size="10">1</text>
<text x="140" y="286" text-anchor="middle" fill="#94a3b8" font-family="system-ui, sans-serif" font-size="10">2</text>
<text x="200" y="286" text-anchor="middle" fill="#94a3b8" font-family="system-ui, sans-serif" font-size="10">3</text>
<text x="260" y="286" text-anchor="middle" fill="#94a3b8" font-family="system-ui, sans-serif" font-size="10">4</text>
<text x="320" y="286" text-anchor="middle" fill="#94a3b8" font-family="system-ui, sans-serif" font-size="10">5</text>
<text x="210" y="298" text-anchor="middle" fill="#e2e8f0" font-family="system-ui, sans-serif" font-size="11">Games Played (x)</text>
<!-- Y-axis labels (Points) -->
<text x="42" y="253" text-anchor="end" fill="#94a3b8" font-family="system-ui, sans-serif" font-size="10">5</text>
<text x="42" y="213" text-anchor="end" fill="#94a3b8" font-family="system-ui, sans-serif" font-size="10">10</text>
<text x="42" y="173" text-anchor="end" fill="#94a3b8" font-family="system-ui, sans-serif" font-size="10">15</text>
<text x="42" y="133" text-anchor="end" fill="#94a3b8" font-family="system-ui, sans-serif" font-size="10">20</text>
<text x="42" y="93" text-anchor="end" fill="#94a3b8" font-family="system-ui, sans-serif" font-size="10">25</text>
<text x="18" y="165" text-anchor="middle" fill="#e2e8f0" font-family="system-ui, sans-serif" font-size="11" transform="rotate(-90 18 165)">Points (y)</text>
<!-- The Line: y = 3x + 5 -->
<!-- At x=0: y=5 → (50, 250), At x=5: y=20 → (320, 130) -->
<line x1="50" y1="250" x2="350" y2="106" stroke="#f97316" stroke-width="2.5" filter="url(#glow)"/>
<!-- Data points with basketball icons -->
<circle cx="80" cy="238" r="5" fill="#f97316" filter="url(#glow)"/>
<text x="88" y="234" fill="#e2e8f0" font-family="system-ui, sans-serif" font-size="9">(1, 8)</text>
<circle cx="140" cy="214" r="5" fill="#f97316" filter="url(#glow)"/>
<text x="148" y="210" fill="#e2e8f0" font-family="system-ui, sans-serif" font-size="9">(2, 11)</text>
<circle cx="200" cy="190" r="5" fill="#f97316" filter="url(#glow)"/>
<text x="208" y="186" fill="#e2e8f0" font-family="system-ui, sans-serif" font-size="9">(3, 14)</text>
<circle cx="260" cy="166" r="5" fill="#f97316" filter="url(#glow)"/>
<text x="268" y="162" fill="#e2e8f0" font-family="system-ui, sans-serif" font-size="9">(4, 17)</text>
<circle cx="320" cy="142" r="5" fill="#f97316" filter="url(#glow)"/>
<text x="308" y="136" fill="#e2e8f0" font-family="system-ui, sans-serif" font-size="9">(5, 20)</text>
<!-- Slope annotation -->
<line x1="140" y1="214" x2="200" y2="214" stroke="#4ade80" stroke-width="1" stroke-dasharray="4,2"/>
<line x1="200" y1="214" x2="200" y2="190" stroke="#4ade80" stroke-width="1" stroke-dasharray="4,2"/>
<text x="170" y="226" text-anchor="middle" fill="#4ade80" font-family="system-ui, sans-serif" font-size="10">run = 1</text>
<text x="214" y="205" fill="#4ade80" font-family="system-ui, sans-serif" font-size="10">rise = 3</text>
<!-- Y-intercept callout -->
<circle cx="50" cy="250" r="4" fill="#ec4899"/>
<text x="60" y="262" fill="#ec4899" font-family="system-ui, sans-serif" font-size="10">b = 5 (starting pts)</text>
<!-- Legend box -->
<rect x="260" y="56" width="120" height="38" rx="6" fill="#1a1a2e" stroke="rgba(249,115,22,0.3)" stroke-width="1"/>
<text x="270" y="72" fill="#f97316" font-family="system-ui, sans-serif" font-size="10" font-weight="bold">m = 3</text>
<text x="310" y="72" fill="#94a3b8" font-family="system-ui, sans-serif" font-size="10">(+3 pts/game)</text>
<text x="270" y="88" fill="#ec4899" font-family="system-ui, sans-serif" font-size="10" font-weight="bold">b = 5</text>
<text x="300" y="88" fill="#94a3b8" font-family="system-ui, sans-serif" font-size="10">(starting points)</text>
</svg>
This produces a dark-themed coordinate plane showing the line y = 3x + 5 framed as "Points Per Game." The basketball scoreboard header sets the theme, the slope is annotated with rise/run in green, the y-intercept is called out in pink, and data points glow in the basketball orange accent. Every visual element teaches: slope means "+3 points per game," intercept means "started with 5 points."