Feature Matrix Template: AI Turns Screenshots into Competitive Insights

What is an AI-powered competitive feature matrix?

An AI-powered competitive feature matrix is a structured table mapping product capabilities against competitors, built by running screenshots through a Vision API to extract features, normalizing them into a shared taxonomy, and analyzing coverage percentages — replacing 40–60 hours of manual analyst work with a 5–6 hour automated pipeline costing $20–33.

TL;DR

  • -The pipeline has five verifiable stages: collect screenshots by competitor → Vision API extraction → normalization into shared taxonomy → matrix assembly → insight analysis; each stage produces an intermediate file you can inspect and rerun independently.
  • -Coverage above 80% means table stakes — missing any of those features is a dealbreaker for buyers entering the market.
  • -The normalization prompt is critical: raw Vision API output contains synonyms like dark_mode/night_theme/dark_theme; without a canonical vocabulary the matrix is unanalyzable.
  • -Analyzing 50 competitors costs $20–33 in API calls and 5–6 hours total vs. $2,000–3,000 and a full analyst week for equivalent manual work.
  • -Running the pipeline quarterly and diffing successive matrices reveals where competitors are heading — which features are scaling (emerging trends) and which are disappearing (dying patterns).

Manual competitive analysis takes 40–60 hours per iteration. 50 products, each with 10–15 screens, each screen with 5–20 features — that’s 2,500–15,000 data points. An analyst spends a week; the result is outdated in a month.

An AI pipeline does the same work in 5–6 hours, most of which goes into collecting screenshots. Competitor screenshots pass through the Vision API, become structured data, and fill the matrix automatically. This article walks through every step.

What a Feature Matrix Is and Why You Need One

A feature matrix is a table where rows are product features and columns are competitors. Each cell holds a status: present/absent/partial, plus implementation details.

Three use cases:

Product strategy. The matrix shows which features have become table stakes (everyone has them) and which are still differentiators. If 48 out of 50 competitors offer SSO, that’s not an advantage. It’s a requirement.

Positioning. When you can see the whole playing field, finding an unoccupied niche gets easier. Every competitor offers email integration, but only two support Telegram. That’s a signal.

Backlog prioritization. Instead of guesswork — data. Feature X exists at 80% of competitors, users are asking for it, and you don’t have it. That’s not a “nice to have.” It’s a gap.

Pipeline Architecture: From Screenshot to Insight

The pipeline has five stages. Each does one thing and passes its result to the next.

Screenshots (PNG/JPG)


[1. Collection] → Organize by competitor and screen


[2. Vision API] → Extract UI elements, text, features


[3. Normalization] → Unified taxonomy, deduplication


[4. Matrix Assembly] → Populate the matrix with data


[5. Analysis] → Patterns, gaps, recommendations

The key principle: each stage produces a verifiable intermediate result. It’s not a black box where screenshots go in and insights come out. Every step can be inspected, corrected, and rerun on its own.

Stage 1: Collecting and Organizing Screenshots

The folder structure determines the quality of the whole analysis. A flat folder with 500 files turns the pipeline into chaos. A hierarchical structure preserves context.

competitors/
├── competitor-a/
│   ├── 01-dashboard.png
│   ├── 02-settings.png
│   ├── 03-billing.png
│   └── metadata.json
├── competitor-b/
│   ├── 01-dashboard.png
│   ├── 02-onboarding.png
│   └── metadata.json
└── competitor-c/
    └── ...

The metadata.json file contains context that the Vision API won’t extract from the screenshot:

{
  "name": "Competitor A",
  "url": "https://competitor-a.com",
  "pricing_tier": "Pro ($49/mo)",
  "capture_date": "2026-03-25",
  "platform": "web",
  "notes": "Captured on 1920x1080, Chrome, English locale"
}

Screenshot sources: your own accounts (trial/freemium), demo videos on YouTube (frame-by-frame export), marketing pages, reviews on G2/Capterra (often contain real UI screenshots).

For frame-by-frame export from video, ffmpeg works well:

ffmpeg -i demo-video.mp4 -vf "fps=1/5,select='gt(scene,0.3)'" \
  -vsync vfr competitor-a/frame_%04d.png

This extracts one frame every 5 seconds, but only when the scene has changed by more than 30%. This filters out static moments and duplicates.

Stage 2: Data Extraction via Vision API

Claude Vision processes screenshots and pulls out structured information. The key point: the prompt determines extraction quality. A generic “describe what you see” prompt produces garbage. A structured prompt produces data.

Prompt for extracting features from a UI screenshot

Analyze this product screenshot. Extract ALL visible features,
UI elements and capabilities.

For each feature found, provide:
1. feature_name: Short, standardized name (e.g., "dark_mode",
   "two_factor_auth", "csv_export")
2. category: One of [navigation, data_display, data_input,
   user_management, integrations, analytics, collaboration,
   settings, billing, notifications, onboarding]
3. implementation_detail: How exactly it's implemented
   (e.g., "Toggle in top-right corner", "Dropdown with 5 options")
4. ui_pattern: The UI pattern used (e.g., "modal", "sidebar",
   "inline_edit", "wizard", "tab_panel")
5. confidence: high/medium/low — how certain you are this
   feature exists based on the screenshot

Return ONLY valid JSON array. No commentary.

Example output:
[
  {
    "feature_name": "dark_mode",
    "category": "settings",
    "implementation_detail": "Toggle switch in header, currently OFF",
    "ui_pattern": "toggle",
    "confidence": "high"
  }
]

This prompt works for three reasons. It enforces a rigid output schema. It locks categories to a fixed list — critical for normalization in the next stage. And it requires a confidence level, so you can filter unreliable results.

Prompt for extracting text content (OCR task)

Extract ALL visible text from this screenshot.
Organize by UI region.

Return JSON:
{
  "header": ["text items in header area"],
  "navigation": ["menu items, tabs"],
  "main_content": ["headings, body text, labels"],
  "sidebar": ["sidebar text if present"],
  "footer": ["footer text"],
  "buttons": ["all button labels"],
  "tooltips": ["any visible tooltip text"],
  "errors_warnings": ["error messages, warnings, notices"]
}

Include placeholder text (e.g., "Enter your email...")
and microcopy. These reveal UX decisions.

Microcopy is a source of insights most analysts skip. A placeholder reading “Search across 10,000+ integrations” tells you more about positioning than the marketing page does.

Batch processing via API

import anthropic
import json
import base64
from pathlib import Path

client = anthropic.Anthropic()

FEATURE_EXTRACTION_PROMPT = """..."""  # prompt above

def extract_features(image_path: str, competitor: str, screen: str):
    with open(image_path, "rb") as f:
        image_data = base64.standard_b64encode(f.read()).decode("utf-8")

    ext = Path(image_path).suffix.lstrip(".")
    media_type = f"image/{'jpeg' if ext == 'jpg' else ext}"

    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=4096,
        messages=[{
            "role": "user",
            "content": [
                {
                    "type": "image",
                    "source": {
                        "type": "base64",
                        "media_type": media_type,
                        "data": image_data,
                    },
                },
                {"type": "text", "text": FEATURE_EXTRACTION_PROMPT}
            ],
        }],
    )

    features = json.loads(response.content[0].text)
    for feat in features:
        feat["competitor"] = competitor
        feat["screen"] = screen
        feat["source_image"] = image_path

    return features


def process_all_competitors(base_dir: str):
    all_features = []
    base = Path(base_dir)

    for competitor_dir in sorted(base.iterdir()):
        if not competitor_dir.is_dir():
            continue

        competitor = competitor_dir.name
        for img in sorted(competitor_dir.glob("*.png")):
            screen = img.stem
            print(f"Processing {competitor}/{screen}...")

            features = extract_features(str(img), competitor, screen)
            all_features.extend(features)

    return all_features


results = process_all_competitors("./competitors")
with open("raw_features.json", "w") as f:
    json.dump(results, f, indent=2)

print(f"Extracted {len(results)} features from all competitors")

With 50 competitors and 10 screenshots each, that’s 500 API calls. On Claude Sonnet: roughly ~$15–25 depending on image sizes. Processing takes 30–45 minutes with parallel requests.

To speed things up, add asyncio and cap concurrency at 10 simultaneous requests to stay within rate limits.

Stage 3: Normalization and Taxonomy Creation

Raw Vision API data comes with duplicates and inconsistencies. “dark_mode,” “night_theme,” “dark_theme” — three names for the same feature. Normalization brings everything to a single vocabulary.

Normalization prompt

You are a product analyst normalizing feature names from
competitive analysis data.

Input: Array of raw features extracted from competitor screenshots.

Tasks:
1. Merge duplicates: features that describe the same capability
   but use different names → single canonical name
2. Standardize naming: snake_case, English, descriptive
   (e.g., "Dark Mode toggle" → "dark_mode")
3. Assign to taxonomy categories (2-level hierarchy):
   - authentication: [sso, mfa, password_policy, social_login, ...]
   - collaboration: [real_time_editing, comments, mentions, sharing, ...]
   - analytics: [dashboard, custom_reports, export, scheduled_reports, ...]
   - integrations: [api, webhooks, zapier, native_integrations, ...]
   - ... (extend as needed based on the data)
4. Flag conflicts: if same feature appears differently across
   competitors, note it

Return JSON:
{
  "taxonomy": {
    "category_name": {
      "feature_canonical_name": {
        "aliases": ["all names this was called in raw data"],
        "description": "one sentence",
        "competitors": {
          "competitor-a": {
            "status": "full|partial|absent",
            "detail": "implementation notes",
            "confidence": "high|medium|low"
          }
        }
      }
    }
  },
  "conflicts": [...],
  "stats": {
    "total_unique_features": N,
    "total_categories": N,
    "duplicates_merged": N
  }
}

This stage can run in a single call if the full dataset fits the context window. For 50 competitors with 10 screens each, you’re looking at ~2,500–5,000 records — roughly 500K–1M tokens. Current Claude models with a 1M-token context window can take that volume in one call, but the output token limit becomes the bottleneck: a taxonomy covering thousands of records won’t fit in a single response. The fix is the same: split into batches by category and merge the results.

def normalize_in_batches(raw_features, batch_size=200):
    batches = [
        raw_features[i:i+batch_size]
        for i in range(0, len(raw_features), batch_size)
    ]

    normalized_batches = []
    for batch in batches:
        result = call_claude_normalize(batch)  # call with prompt above
        normalized_batches.append(result)

    return merge_taxonomies(normalized_batches)

Stage 4: Assembling the Feature Matrix

Normalized data becomes a matrix. Format depends on the audience: CSV/Excel for analysts, JSON for automation, Markdown/HTML for presentations.

Matrix template (Markdown)

| Feature | Cat. | Comp. A | Comp. B | Comp. C | Coverage |
|---------|------|---------|---------|---------|----------|
| SSO (SAML) | Auth | ✅ Full | ✅ Full | ❌ | 67% |
| MFA | Auth | ✅ TOTP | ✅ TOTP+SMS | ✅ TOTP | 100% |
| Dark mode | UI | ✅ | ❌ | ✅ | 67% |
| CSV export | Data | ✅ | ✅ | ✅ Limited | 100% |
| Real-time collab | Collab | ❌ | ✅ | ❌ | 33% |
| API (REST) | Integ. | ✅ v2 | ✅ v3 | ✅ v1 | 100% |
| Webhooks | Integ. | ✅ | ❌ | ✅ | 67% |
| Custom reports | Analytics | ✅ | ✅ Drag&drop | ❌ | 67% |

The Coverage column shows what percentage of competitors have the feature. That’s the key metric. Above 80% means table stakes. Below 20% is either a potential differentiator or a niche feature nobody’s bothered with.

Generating the matrix from normalized data

import csv
from collections import defaultdict

def build_matrix(taxonomy: dict, competitors: list[str]):
    rows = []

    for category, features in taxonomy["taxonomy"].items():
        for feature_name, feature_data in features.items():
            row = {
                "feature": feature_name,
                "category": category,
            }

            present_count = 0
            for comp in competitors:
                comp_data = feature_data["competitors"].get(comp, {})
                status = comp_data.get("status", "absent")
                detail = comp_data.get("detail", "")

                if status == "full":
                    row[comp] = f"Yes: {detail}" if detail else "Yes"
                    present_count += 1
                elif status == "partial":
                    row[comp] = f"Partial: {detail}" if detail else "Partial"
                    present_count += 0.5
                else:
                    row[comp] = "No"

            row["coverage"] = f"{present_count / len(competitors) * 100:.0f}%"
            rows.append(row)

    rows.sort(key=lambda r: r["coverage"], reverse=True)
    return rows


def export_csv(rows, competitors, output_path):
    fieldnames = ["feature", "category"] + competitors + ["coverage"]
    with open(output_path, "w", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        writer.writeheader()
        writer.writerows(rows)

Stage 5: Analysis and Insight Extraction

The matrix is just data. Insights require interpretation. AI handles this too — but only with the right prompt.

Prompt for feature matrix analysis

You are a product strategist analyzing a competitive
feature matrix.

Input: Feature matrix with {N} competitors and {M} features.

Perform the following analyses:

1. TABLE STAKES (coverage >= 80%)
   List features every competitor has. These are non-negotiable.
   Missing any = dealbreaker for buyers.

2. DIFFERENTIATORS (coverage 20-50%)
   Features only some competitors offer. These create
   competitive advantage. Rank by strategic value.

3. WHITE SPACES (coverage < 20% or 0%)
   Features almost nobody offers. Evaluate:
   - Is this a missed opportunity or deliberately ignored?
   - What user problem would it solve?
   - Effort estimate: low/medium/high

4. IMPLEMENTATION PATTERNS
   For features present across competitors, compare
   implementation approaches. Which UX patterns dominate?
   Which are unique?

5. COMPETITIVE CLUSTERS
   Group competitors by feature similarity. Which competitors
   are direct substitutes? Which serve different segments?

6. RECOMMENDATIONS
   Based on the analysis, provide 5 actionable recommendations
   for a product team entering this market. Each must include:
   - What to build
   - Why (data from matrix)
   - Priority: must-have / should-have / nice-to-have

Return structured JSON with all 6 sections.

Sample insights output

A model example of what analyzing a 50-competitor matrix in the project management category might produce (numbers are illustrative):

Table stakes (coverage 80%+): Kanban board, task assignments, due dates, file attachments, email notifications, mobile app, Slack integration, CSV export. Users treat these as baseline. Enter the market without them and you’re already behind.

Differentiators (coverage 20–50%): Time tracking (38%), resource planning (26%), custom workflows (34%), client portal (22%). Each serves a specific segment. Time tracking matters to agencies. Client portals matter to freelancers and outsourcing teams.

White spaces (coverage <20%): AI-assisted task prioritization (8%), automatic dependency detection (4%), burnout risk alerts (2%). Each is either an opportunity or a deliberate market choice. Either way, user validation is needed before building.

Automation and Repeatability

Competitive analysis isn’t a one-time project. The market shifts every quarter. A pipeline built once can rerun with minimal effort.

Full-cycle script

def run_pipeline(competitors_dir: str, output_dir: str):
    # Stage 1: structure validation
    validate_directory_structure(competitors_dir)

    # Stage 2: Vision API extraction
    raw_features = process_all_competitors(competitors_dir)
    save_json(raw_features, f"{output_dir}/01_raw_features.json")

    # Stage 3: normalization
    taxonomy = normalize_in_batches(raw_features)
    save_json(taxonomy, f"{output_dir}/02_taxonomy.json")

    # Stage 4: matrix
    competitors = list_competitor_names(competitors_dir)
    matrix = build_matrix(taxonomy, competitors)
    export_csv(matrix, competitors, f"{output_dir}/03_matrix.csv")

    # Stage 5: analysis
    insights = analyze_matrix(matrix, competitors)
    save_json(insights, f"{output_dir}/04_insights.json")

    # Report generation
    report = generate_report(matrix, insights)
    save_markdown(report, f"{output_dir}/05_report.md")

    print(f"Pipeline complete. {len(raw_features)} features extracted.")
    print(f"Results in {output_dir}/")

Every intermediate result gets saved to a file. If the Vision API failed on competitor 30, you don’t restart from scratch. Process the remaining 20 and merge with what you already have.

Cost and Time

Ballpark estimates for analyzing 50 competitors:

StageTimeAPI Cost
Screenshot collection3–5 hours (manual)$0
Vision API extraction30–45 min$15–25
Normalization10–15 min$3–5
Matrix assembly<1 min (script)$0
Analysis and insights5–10 min$2–3
Total~5–6 hours$20–33

For comparison: an analyst spends 40–60 hours on the same work. At $50/hour, that’s $2,000–3,000. A 60–100x cost difference and 8–10x time savings.

The slowest stage is screenshot collection — and it can be partially automated with Playwright:

from playwright.async_api import async_playwright

async def capture_competitor(url: str, output_dir: str):
    async with async_playwright() as p:
        browser = await p.chromium.launch()
        page = await browser.new_page(viewport={"width": 1920, "height": 1080})
        await page.goto(url)

        # Screenshot of the main page
        await page.screenshot(path=f"{output_dir}/01-landing.png",
                              full_page=True)

        # Navigate through main sections
        nav_links = await page.query_selector_all("nav a")
        for i, link in enumerate(nav_links[:10]):
            text = await link.inner_text()
            await link.click()
            await page.wait_for_load_state("networkidle")
            await page.screenshot(
                path=f"{output_dir}/{i+2:02d}-{text.lower().replace(' ', '-')}.png"
            )

        await browser.close()

Works for marketing pages and demos. For logged-in sections, you’ll need a trial account and manual capture.

Common Mistakes and How to Avoid Them

Mistake 1: granularity too fine. “Button is blue” is not a feature. “Bulk actions support” is. The Vision API extracts everything indiscriminately — the prompt must define the abstraction level.

Mistake 2: no pricing tier context. Is the feature on Enterprise at $500/month or on Free? Those are entirely different situations. The metadata file for each competitor should include the pricing tier. Every feature ties to a plan.

Mistake 3: running it once. A matrix created today is outdated in 2–3 months. Run the pipeline quarterly. The diff between two matrices tells you where competitors are heading.

Mistake 4: analysis without action. 300 rows without prioritization is noise. Stage 5 isn’t optional. It’s what turns data into decisions.

Pipeline Extensions

The base pipeline covers the essentials. A few extensions make it considerably more powerful.

Pricing intelligence. Run pricing data collection in parallel: tier names, limits, costs. This lets you build price-to-feature ratios and spot overpriced or underpriced positions in the market.

UX pattern library. Beyond features, the Vision API captures UI patterns. Compiled into a library, those patterns reveal what the industry has converged on. Useful for designers: no need to reinvent interactions that users already expect.

Temporal analysis. Running the pipeline quarterly creates a time series. You can see which features competitors are adding at scale (emerging trends) and which are quietly disappearing (dying patterns).

Pairing with context engineering. A feature matrix is ideal context for an AI assistant in product work. Load it into the prompt system and you can generate PRDs, user stories, and technical specs with the competitive landscape already factored in.

Conclusion

A feature matrix isn’t a document. It’s a decision-making tool. The AI pipeline removes the main obstacle: how long it takes to collect and structure the data.

Five steps: collect screenshots, extract features via Vision API, normalize, assemble the matrix, analyze. Each step produces a verifiable result. Total: 5–6 hours and $20–33 instead of a week of analyst work.

The matrix answers three questions. What you can’t ship without (table stakes). What creates real competitive advantage (differentiators). Where an opportunity nobody noticed might be hiding (white spaces). That’s enough to make grounded product decisions.


Need help building an AI-powered competitive analysis pipeline? I help startups build AI products and automate processes — belov.works.

FAQ

How do you handle competitors whose product is behind a login wall and can’t be captured with Playwright?

The practical workaround is a combination of trial accounts and structured manual capture. Create trial accounts for competitors that offer them, log in, and take screenshots against a predefined set of screens: dashboard, settings, main workflow screens, and billing page. Store these with the same directory structure and naming convention as automated captures — the Vision API pipeline doesn’t care how the screenshots were collected. For competitors with no trial access, use G2, Capterra, and Trustpilot review screenshots (reviewers often attach product screenshots), public demo videos (extract keyframes), and the competitor’s own help documentation, which typically includes screenshots of every feature.

What’s the right update cadence for a feature matrix, and how do you diff two matrices over time?

Quarterly is the practical minimum for fast-moving SaaS markets; bi-annual is sufficient for enterprise software with longer release cycles. The diff is straightforward at the data level: compare the taxonomy JSON from two runs and look for new canonical feature names (competitor added something), status changes from “absent” to “partial” or “full” (competitor shipped something), and coverage percentage shifts across categories. A simple Python script that loads two taxonomy files and outputs a diff report is more reliable than visual comparison. Treating the quarterly diff as an artifact — “Q1 2025 vs Q2 2025 changes” — gives product teams a structured input for roadmap reviews rather than a static snapshot that goes stale.

How do you decide which features to extract at what level of granularity when writing the Vision API prompt?

The granularity decision should match how you make product decisions. If you’re determining whether to build a feature at all, coarse granularity is correct — “team collaboration” rather than “comment threading with @mentions and emoji reactions.” If you’re deciding how to implement a feature that’s already on the roadmap, fine granularity matters — “real-time co-editing vs. locked-document collaboration vs. async commenting.” Running two passes with different abstraction levels on the same screenshot set is often the best approach: the coarse pass fills the matrix for strategic analysis, and the fine pass on specific feature categories provides implementation research for the team building it.