RepoPilotOpen in app →

fogleman/primitive

Reproducing images with geometric primitives.

Mixed

Stale — last commit 2y ago

weakest axis
Use as dependencyConcerns

last commit was 2y ago; top contributor handles 94% of recent commits…

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isMixed

last commit was 2y ago; no CI workflows detected

  • 6 active contributors
  • MIT licensed
  • Stale — last commit 2y ago
Show all 6 evidence items →
  • Single-maintainer risk — top contributor 94% of recent commits
  • No CI workflows detected
  • No test directory detected
What would change the summary?
  • Use as dependency ConcernsMixed if: 1 commit in the last 365 days
  • Deploy as-is MixedHealthy if: 1 commit in the last 180 days

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Forkable" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Forkable
[![RepoPilot: Forkable](https://repopilot.app/api/badge/fogleman/primitive?axis=fork)](https://repopilot.app/r/fogleman/primitive)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/fogleman/primitive on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: fogleman/primitive

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/fogleman/primitive shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

WAIT — Stale — last commit 2y ago

  • 6 active contributors
  • MIT licensed
  • ⚠ Stale — last commit 2y ago
  • ⚠ Single-maintainer risk — top contributor 94% of recent commits
  • ⚠ No CI workflows detected
  • ⚠ No test directory detected

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live fogleman/primitive repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/fogleman/primitive.

What it runs against: a local clone of fogleman/primitive — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in fogleman/primitive | Confirms the artifact applies here, not a fork | | 2 | License is still MIT | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | Last commit ≤ 684 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>fogleman/primitive</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of fogleman/primitive. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/fogleman/primitive.git
#   cd primitive
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of fogleman/primitive and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "fogleman/primitive(\\.git)?\\b" \\
  && ok "origin remote is fogleman/primitive" \\
  || miss "origin remote is not fogleman/primitive (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
  && ok "license is MIT" \\
  || miss "license drift — was MIT at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 684 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~654d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/fogleman/primitive"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

Primitive is a Go application that reconstructs images using iterative geometric shape placement (triangles, rectangles, ellipses, circles, polygons, Bézier curves). It minimizes mean squared error between the target and rendered image by greedily adding one optimal shape at a time, typically producing artistic results with 50–200 shapes. The core algorithm is language-agnostic but the main engine is in Go with a Python bot wrapper for Twitter integration. Dual-language monorepo: core engine in primitive/ (Go packages: color.go, shape.go, optimize.go, raster.go, model.go handling the main loop; worker.go for parallelism); CLI entry at main.go; separate bot/ directory (Python, independent Twitter integration); examples/ with sample images; scripts/ with utility tools (html.py for rendering results, process.py for batch operations). No shared state between Go and Python—they operate independently.

👥Who it's for

Artists and generative art enthusiasts who want to convert raster images into minimalist geometric representations; academic researchers studying optimization and constraint-based rendering; Twitter bot operators running bot/main.py to auto-generate and post abstracted images. Secondary audience: Go developers interested in computational geometry and image processing.

🌱Maturity & risk

Mature and stable but archived/dormant: the repo shows solid foundational work with ~44KB of Go code across well-organized primitive modules, but no recent commit history is visible in the file list. The command-line interface is fully documented with 8 shape modes and multiple output formats (PNG, SVG, GIF, JPG). No automated tests are evident in the file structure, suggesting it relies on manual validation.

Low technical risk for core algorithm (Go is self-contained), but moderate operational risk: the Twitter bot depends on external APIs (Flickr, Twitter) and Python 3.x compatibility with pinned but aging dependencies (python-twitter==3.1 from circa 2016, requests==2.11.1). No CI/CD pipeline visible; single-maintainer project with no evidence of issue triage or PR review process. Breaking changes in ImageMagick's convert command could break GIF output.

Active areas of work

No active development visible from the file listing alone, but the repo is feature-complete: main.go supports all 8 shape modes (combo, triangle, rect, ellipse, circle, rotatedrect, beziers, polygon, rotatedellipse), parallel workers, custom alpha blending, and resizing. The Twitter bot is fully integrated but likely dormant given the old dependency versions. Maintenance is minimal.

🚀Get running

Clone and build the Go CLI:

git clone https://github.com/fogleman/primitive.git
cd primitive
go build -o primitive main.go
./primitive -i examples/lenna.png -o output.png -n 100 -m 1

For the Twitter bot, install Python dependencies:

cd bot
pip install -r requirements.txt
python main.py

Daily commands: CLI mode: ./primitive -i input.png -o output.png -n 100 -m 1 -s 1024. Flags: -i (input file, required), -o (output file, required), -n (shape count, required), -m (mode 0–8, default 1 for triangles), -r (resize input to 256px default), -s (output size, default 1024), -a (alpha, default 128), -j (workers, default all cores), -v/-vv (verbosity). Bot: cd bot && python main.py (requires Twitter/Flickr API keys in environment). Generate animation: use .gif extension in -o filename (requires ImageMagick convert in PATH).

🗺️Map of the codebase

  • primitive/model.go: Core state machine: holds the current rendered image and target, orchestrates shape addition loop via Model.Step()
  • primitive/optimize.go: Hill-climbing optimizer: searches shape space to find the single best shape placement that minimizes error
  • primitive/worker.go: Parallel search: distributes candidate shape evaluation across CPU cores via goroutine pool
  • primitive/raster.go: Rendering pipeline: converts abstract shapes to raster with alpha blending and scanline fill
  • primitive/shape.go: Shape interface abstraction: defines contract all primitives (triangle, ellipse, etc.) must implement
  • main.go: CLI orchestrator: parses flags, loads image, invokes Model, handles output serialization (PNG/SVG/GIF/JPG)
  • bot/main.py: Twitter bot: polls Flickr API for images, shells to Go binary, posts results via Twitter API
  • primitive/heatmap.go: Error heatmap: visualizes per-pixel difference for targeted shape placement in low-error regions

🛠️How to make changes

To add a new shape type: implement the Shape interface in primitive/shape.go, create a new file (e.g., primitive/hexagon.go), and register in mode selection logic in main.go around the mode switch statement. To optimize the scoring heuristic: modify primitive/optimize.go (contains the hill-climbing search) and primitive/heatmap.go (error visualization). To add output format: extend file extension detection in main.go's output handling. To tune algorithm parameters: modify constants in primitive/core.go and worker iteration counts in primitive/worker.go. To debug rendering: study primitive/raster.go and primitive/scanline.go.

🪤Traps & gotchas

ImageMagick convert command must be in PATH to generate GIF output (no fallback or validation). Twitter bot requires valid Flickr and Twitter API credentials in environment (hardcoded or undocumented). Python bot depends on python-twitter v3.1 which is ancient (circa 2016) and likely broken with modern Twitter API v2. Go binary expects input image to be readable from disk; no stdin support. Output SVG format is not configurable (no option to set stroke width or simplify paths). Alpha blending logic in raster.go assumes 8-bit color; behavior with 16-bit or float images undefined. Parallelism default uses runtime.NumCPU() but can deadlock if -j is set to 0 (interpreted as 'use all cores', not 'disable parallelism').

💡Concepts to learn

  • Hill Climbing / Local Search Optimization — Primitive's core algorithm in optimize.go uses greedy hill-climbing to find locally-optimal shape placement without global search; understanding this explains why results are fast but may not be globally optimal.
  • Scanline Rasterization — scanline.go implements polygon fill via scanline algorithm, essential for efficiently converting vector primitives (triangles, polygons) into raster pixels with correct fill ordering.
  • Mean Squared Error (MSE) / Perceptual Loss — Primitive minimizes MSE between target and rendered images; understanding this metric explains why some reconstructions look abstractly correct but fail on fine details.
  • Alpha Blending / Compositing — raster.go's rendering pipeline uses alpha blending to overlay shapes; this is critical for understanding color accumulation and why the -a flag affects visual density.
  • Worker Pool Concurrency Pattern — worker.go distributes shape candidate evaluation across goroutines using a fixed pool; this is why Primitive scales to multicore CPUs and why -j flag controls parallelism.
  • Heatmap-Driven Search — heatmap.go prioritizes shape placement in high-error regions rather than random search; this accelerates convergence and explains why error visualization matters.
  • Affine Transformations — Rectangle and ellipse shapes support rotation via affine transforms; understanding matrix-based rotation is key to tuning rotatedrect and rotatedellipse modes.
  • jasonalice/primitive.js — JavaScript/Canvas port of the same algorithm, allowing browser-based image processing without Go CLI
  • ondras/geometric-svg — Similar geometric shape optimization library for SVG output; compatible tooling for vector rendering workflows
  • go-echarts/go-echarts — Go visualization library for rendering complex shapes and animations; useful for extending Primitive's output formats
  • golang/image — Official Go image processing library that Primitive depends on for PNG/JPEG decoding and raster manipulation
  • microflo/imgui-go — Go UI library used by Primitive's macOS desktop app (primitive.lol) for interactive parameter tuning and live preview

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add unit tests for primitive Go packages (color, shape, optimize, raster)

The core algorithm logic in primitive/.go has no visible test files (_test.go). Testing the geometric primitive operations (ellipse fitting, polygon rendering, error calculation in optimize.go) would catch regressions and help new contributors understand expected behavior of shapes and color handling.

  • [ ] Create primitive/color_test.go with tests for color space conversions and distance calculations
  • [ ] Create primitive/shape_test.go to verify Shape interface implementations (triangle, rectangle, ellipse, polygon)
  • [ ] Create primitive/optimize_test.go to test the score calculation and mutation logic
  • [ ] Create primitive/raster_test.go to verify shape rendering produces correct pixel output
  • [ ] Run 'go test ./primitive' to ensure all tests pass

Add GitHub Actions CI workflow for Go linting and builds

The repo has no .github/workflows directory for automated testing. A CI pipeline would validate Go code quality, ensure builds pass on multiple platforms (linux, macOS, windows), and catch issues before merging. This is critical for a binary tool.

  • [ ] Create .github/workflows/go-test.yml that runs 'go fmt', 'go vet', and 'go test ./...'
  • [ ] Add build matrix for os: [ubuntu-latest, macos-latest, windows-latest]
  • [ ] Add step to build main.go binary to verify cross-platform compilation
  • [ ] Add linter step using golangci-lint for code quality checks
  • [ ] Test workflow by pushing to a branch and verifying all checks pass

Document command-line flags and add example usage in README.md

The README has a '### Command-line Usage' heading but the section is empty. main.go likely has flag definitions (e.g., -in, -out, -n for number of shapes) that need documentation with examples showing basic usage, parameter tuning, and output formats.

  • [ ] Review main.go to extract all flag definitions and their defaults
  • [ ] Add 'Command-line Usage' section with syntax: 'primitive [options] -in input.png -out output.png'
  • [ ] Document key flags: -in (input), -out (output), -n (shape count), -m (mode/shape type), -j (workers)
  • [ ] Add 3-5 concrete examples (e.g., 'primitive -in photo.jpg -out result.png -n 100 -m 3')
  • [ ] Add troubleshooting section for common issues (file format, resource limits)

🌿Good first issues

  • Add unit tests for primitive/color.go (ColorAverage, Color.lerp) and primitive/polygon.go (Polygon.Bounds, Polygon.Contains) — currently no *_test.go files exist, making regression risky when refactoring color handling or polygon clipping.
  • Document the exact SVG output schema and add example SVG output to examples/ folder — README lists SVG as a format but no example SVG is provided, and main.go's SVG serialization logic is opaque (likely in model.go's SvgString method) with no format specification.
  • Update bot/requirements.txt to use python-twitter v4.x or replace with tweepy/alternative, and add CI test that validates bot.py can at least authenticate without API calls — current dependencies are 8+ years old and likely incompatible with current Twitter/Flickr APIs.

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 0373c21 — Merge pull request #77 from larryng/master (fogleman)
  • 673f57e — Merge pull request #71 from sylvaindumont/pr (fogleman)
  • 09a9e9f — fix mutating ellipse ry (larryng)
  • b0cb275 — allow input from stdin and output to stdout (sylvaindumont)
  • 69506c9 — Merge pull request #55 from linkerlin/patch-1 (fogleman)
  • 46089ee — Fixed #44 (linkerlin)
  • 80f39ce — Merge pull request #42 from zwade/master (fogleman)
  • 11a9f18 — Add support for transparency (zwade)
  • 37639cc — Update README.md (fogleman)
  • da8931e — Merge pull request #39 from mmlkrx/update-readme-and-cli-help (fogleman)

🔒Security observations

This codebase has significant security concerns, primarily driven by severely outdated dependencies from 2011-2016 that contain multiple known vulnerabilities. The Twitter/Flickr bot component has the highest risk due to age, potential credential exposure, and processing of untrusted user inputs. Immediate action required: update all Python dependencies to current versions, implement environment-based credential management, add input validation for image processing, and establish ongoing dependency maintenance practices.

  • High · Outdated Dependency: requests==2.11.1 — bot/requirements.txt. The requests library version 2.11.1 (released in 2016) contains multiple known security vulnerabilities including CVE-2018-18074 (unintended leak of Proxy-Authorization header), CVE-2023-32681 (potential HTTP request smuggling), and other issues. This version is severely outdated and should be updated to the latest stable version. Fix: Update requests to the latest version (2.31.0 or newer). Run: pip install --upgrade requests
  • High · Outdated Dependency: python-twitter==3.1 — bot/requirements.txt. The python-twitter library version 3.1 (released in 2013) is extremely outdated and likely contains unpatched security vulnerabilities. The library has had multiple releases since then with security fixes and improvements. This version may have authentication and API handling issues. Fix: Update python-twitter to the latest version available. Check the project repository and update to a recent stable version. Run: pip install --upgrade python-twitter
  • Medium · Potential Hardcoded Credentials in Twitter Bot — bot/main.py. The bot/main.py file is part of a Twitter bot that interacts with the Twitter API and Flickr API. Without seeing the actual code, there is a high risk that API keys, access tokens, or authentication credentials may be hardcoded in the source file or configuration, especially given the age of the dependencies. Fix: Ensure all API credentials are loaded from environment variables or secure configuration files (e.g., using python-dotenv). Never hardcode secrets in source code. Implement secure credential management.
  • Medium · No Version Pinning Precision — bot/requirements.txt. The requirements.txt uses exact versions (==) but these are very old. While exact pinning is good for reproducibility, these ancient versions suggest the project may not be actively maintained or security-patched. Fix: Update all dependencies to their latest stable versions and establish a maintenance cadence for regular security updates. Consider using tools like pip-audit or Dependabot for continuous vulnerability scanning.
  • Low · Lack of Input Validation Indicators — bot/main.py, scripts/process.py. The bot accepts user-submitted images via Twitter (as mentioned in README: 'You can tweet a picture to the bot and it will process it for you'). Without visible input validation in the file structure, there may be risks related to processing untrusted image files. Fix: Implement strict input validation for uploaded images: verify file type/magic numbers, enforce size limits, use sandboxed processing environments, and handle errors gracefully without exposing system information.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Mixed signals · fogleman/primitive — RepoPilot