Text-to-SVG: Generate Vector Graphics from Text Prompts with AI
Generate clean SVG vector graphics from text prompts using diffusion models + vectorization pipelines. No Illustrator, no manual tracing — fully programmable vector output.
SVG generation from text prompts is one of the most underrated applications of AI in design. Instead of rasterizing to a PNG, you can produce scalable vector paths that work at any resolution, integrate with CSS animations, and export directly into Figma or Illustrator.
This guide covers the complete pipeline: text prompt → raster generation → vectorization → clean SVG, with a direct SVG path generation approach using diffusion guidance.
Two Approaches to Text-to-SVG
Approach 1: Raster-to-Vector Pipeline Generate a raster image (PNG) from your prompt, then vectorize it using traced paths. Reliable, works with any image generator.
Approach 2: Direct SVG Generation Use DiffVG or SVGCraft-style methods that optimize SVG paths directly. Higher quality, more complex setup.
We'll cover both.
Setup
pip install diffusers transformers torch Pillow cairosvg svgwrite
# For vectorization
pip install vtracer # Rust-based vectorizer, much faster than potrace
# Or: sudo apt install potrace (classic open-source tracer)
Approach 1: Raster → Vector Pipeline
Step 1: Generate the Raster Image
import torch
from diffusers import StableDiffusionPipeline
from PIL import Image
def generate_raster(
prompt: str,
negative_prompt: str = "photo, realistic, complex background, gradient, noise",
model_id: str = "runwayml/stable-diffusion-v1-5", # Or any local model
steps: int = 30,
guidance: float = 7.5,
size: int = 512
) -> Image.Image:
"""
Generate a raster image optimized for vectorization.
Key: use negative prompts to encourage flat, clean shapes.
"""
pipe = StableDiffusionPipeline.from_pretrained(
model_id,
torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32
)
pipe = pipe.to("cuda" if torch.cuda.is_available() else "cpu")
pipe.enable_attention_slicing()
# Prompt engineering for vectorization-friendly output
vector_prompt = f"{prompt}, flat design, minimal, clean lines, solid colors, icon style, white background, svg illustration"
image = pipe(
prompt=vector_prompt,
negative_prompt=negative_prompt,
num_inference_steps=steps,
guidance_scale=guidance,
height=size,
width=size
).images[0]
return image
# Generate a logo-style image
img = generate_raster(
"a mountain peak with sun rays, minimalist logo",
size=512
)
img.save("mountain_logo.png")
Step 2: Preprocess for Vectorization
Better preprocessing → cleaner vector paths:
from PIL import Image, ImageFilter, ImageEnhance
import numpy as np
def preprocess_for_vectorization(
image: Image.Image,
reduce_colors: int = 8, # Posterize to N colors
sharpen: bool = True,
background_threshold: int = 240 # Pixels lighter than this → pure white
) -> Image.Image:
"""
Clean up raster image to make vectorization produce fewer, cleaner paths.
"""
img = image.convert("RGB")
# Remove near-white background noise
arr = np.array(img)
mask = np.all(arr > background_threshold, axis=2)
arr[mask] = [255, 255, 255]
img = Image.fromarray(arr)
# Posterize to reduce number of distinct color regions
img = img.convert("P", palette=Image.ADAPTIVE, colors=reduce_colors)
img = img.convert("RGB")
if sharpen:
img = img.filter(ImageFilter.SHARPEN)
enhancer = ImageEnhance.Contrast(img)
img = enhancer.enhance(1.5)
return img
clean_img = preprocess_for_vectorization(img, reduce_colors=6)
clean_img.save("mountain_logo_clean.png")
Step 3: Vectorize to SVG
import subprocess
import vtracer
def vectorize_to_svg(
input_path: str,
output_path: str,
method: str = "vtracer", # "vtracer" or "potrace"
color_precision: int = 6, # vtracer: number of color quantization steps
filter_speckle: int = 4, # Remove noise smaller than N pixels
curve_fitting: str = "spline" # spline, polygon, pixel
) -> str:
"""
Convert raster image to SVG using vtracer or potrace.
Returns the SVG content as a string.
"""
if method == "vtracer":
vtracer.convert_image_to_svg_py(
input_path,
output_path,
colormode="color",
hierarchical="stacked",
mode=curve_fitting,
filter_speckle=filter_speckle,
color_precision=color_precision,
layer_difference=16,
corner_threshold=60,
length_threshold=4.0,
max_iterations=10,
splice_threshold=45,
path_precision=3
)
elif method == "potrace":
# Potrace works on BMP, convert first
bmp_path = input_path.replace(".png", ".bmp")
Image.open(input_path).convert("1").save(bmp_path)
subprocess.run([
"potrace", "--svg", "--output", output_path, bmp_path
], check=True)
with open(output_path, "r") as f:
svg_content = f.read()
return svg_content
svg = vectorize_to_svg(
"mountain_logo_clean.png",
"mountain_logo.svg",
method="vtracer",
color_precision=8,
filter_speckle=6
)
print(f"SVG generated: {len(svg)} chars")
Approach 2: Direct SVG Path Generation
For icon-style output, you can generate SVG paths programmatically using geometric primitives guided by an LLM:
import svgwrite
import json
import requests
def generate_svg_with_llm(
prompt: str,
canvas_size: int = 200,
ollama_model: str = "llama3.2:3b"
) -> str:
"""
Use local LLM to describe geometric SVG paths, then render them.
"""
system = f"""You are an SVG designer. Given a description, output ONLY a JSON array of SVG shapes.
Canvas size: {canvas_size}x{canvas_size}. Center is ({canvas_size//2}, {canvas_size//2}).
Shape types:
- circle: {{"type":"circle","cx":100,"cy":100,"r":40,"fill":"#3B82F6"}}
- rect: {{"type":"rect","x":60,"y":60,"width":80,"height":80,"fill":"#F59E0B","rx":8}}
- polygon: {{"type":"polygon","points":"100,20 140,80 60,80","fill":"#10B981"}}
- path: {{"type":"path","d":"M 50 100 L 150 100 L 100 50 Z","fill":"#EF4444"}}
Output JSON array only. 3-8 shapes. No explanation."""
try:
resp = requests.post(
"http://localhost:11434/api/generate",
json={"model": ollama_model, "prompt": prompt, "system": system,
"stream": False, "options": {"temperature": 0.5}},
timeout=20
)
raw = resp.json()["response"]
start = raw.find("[")
end = raw.rfind("]") + 1
shapes = json.loads(raw[start:end])
except Exception:
# Fallback: simple geometric shapes for common prompts
shapes = [
{"type": "circle", "cx": 100, "cy": 100, "r": 60, "fill": "#3B82F6"},
{"type": "circle", "cx": 100, "cy": 100, "r": 40, "fill": "#60A5FA"},
]
# Render shapes to SVG
dwg = svgwrite.Drawing(size=(canvas_size, canvas_size))
dwg.add(dwg.rect(insert=(0, 0), size=(canvas_size, canvas_size), fill="white"))
for shape in shapes:
if shape["type"] == "circle":
dwg.add(dwg.circle(center=(shape["cx"], shape["cy"]), r=shape["r"],
fill=shape.get("fill", "#000")))
elif shape["type"] == "rect":
dwg.add(dwg.rect(insert=(shape["x"], shape["y"]),
size=(shape["width"], shape["height"]),
fill=shape.get("fill", "#000"),
rx=shape.get("rx", 0)))
elif shape["type"] == "polygon":
dwg.add(dwg.polygon(points=shape["points"].split(),
fill=shape.get("fill", "#000")))
elif shape["type"] == "path":
dwg.add(dwg.path(d=shape["d"], fill=shape.get("fill", "#000")))
return dwg.tostring()
svg_content = generate_svg_with_llm("a simple wifi signal icon", canvas_size=200)
with open("wifi_icon.svg", "w") as f:
f.write(svg_content)
print("Icon generated: wifi_icon.svg")
Post-Processing: Clean SVG Optimization
Raw vectorization output is often bloated. Optimize before use:
def optimize_svg(input_path: str, output_path: str):
"""
Simplify SVG using svgo-style path reduction.
Install: npm install -g svgo
"""
result = subprocess.run(
["svgo", "--input", input_path, "--output", output_path,
"--config", '{"plugins": ["removeDoctype", "removeComments", "mergePaths", "convertPathData"]}'],
capture_output=True, text=True
)
before = os.path.getsize(input_path)
after = os.path.getsize(output_path)
reduction = (1 - after/before) * 100
print(f"Optimized: {before:,} → {after:,} bytes ({reduction:.1f}% reduction)")
# Full pipeline
img = generate_raster("a compass rose, flat design icon, navy blue")
clean = preprocess_for_vectorization(img, reduce_colors=4)
clean.save("/tmp/compass_clean.png")
svg = vectorize_to_svg("/tmp/compass_clean.png", "compass.svg")
optimize_svg("compass.svg", "compass_optimized.svg")
Quality Tips
- More reduction colors = fewer SVG paths (faster to trace, simpler output)
filter_speckle=8removes small islands from rough backgrounds- White background in the generated image prevents background paths from bleeding into shapes
- For logos, 2–4 colors at the raster stage produces the cleanest vector result
curve_fitting=splineis best for organic shapes; usepolygonfor geometric/technical icons
The Vector Workspace includes this full text-to-SVG pipeline plus icon set generation, logo creation, image-to-vector conversion, pattern generation, and batch vectorization — 1,015 lines of vector tooling ready to run.