Powerbi motion
Generate a single landscape MP4 video highlighting the most relevant insights from a Microsoft Power BI report. Use whenever the user wants Power BI data, semantic-model measures, or report KPIs turned into motion graphics, animated summaries, or shareable clips for Slack, email, social, or briefings — even without the word animation. Triggers include animate this report, make a video from these KPIs, motion summary, Power BI to video, or any Power BI model + visual-storytelling request.From its SKILL.md
npx -y skills add jamartinezpastor/powerbi-motion --skill powerbi-motionAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
8.9 KB, ~2.0k tokens by cl100k_base, as published. Nobody here has run it
Power BI Motion
Pipeline: discover → propose → render, human-in-the-loop at propose. Never render before the user approves the storyboard.
Output discipline (governs every turn)
This skill produces one MP4, not prose. Per turn the ONLY visible outputs are:
- Stage 2: one framing line (≤ 12 words) + the interactive storyboard widget. Nothing else — no data summaries, no interim findings, no recommendations on which to pick, no commentary after the widget.
- Stage 3: one sentence confirming what was produced + the file link (
present_files) + one next-step line only if the user must act. - A short blocker line only if something real is missing (no MCP, no data, no candidates, no ffmpeg).
Do Stage 1 silently. If a measure is broken, it becomes a storyboard candidate — that IS the surfacing. Target under 200 tokens of visible text for Stage 1+2. If the user asks for analysis/summary of the report instead of animation, this skill is the wrong tool — answer normally without this pipeline.
Prerequisites (verify, then stop if missing)
- Power BI MCP connected (
powerbi-modeling-mcp:*local, or the Remote MCP for published models). Never invent data if absent. - Pillow (
pip install pillow --break-system-packages) and ffmpeg on PATH. That's the entire dependency chain — there is no Node, no npx, no browser, no HyperFrames anymore (seereferences/limitations.mdfor why that was retired).
Report missing prerequisites as one combined message and wait.
Stage 1 — Discover (silent)
- Inventory tables/measures/relationships via MCP listing tools.
- Detect model class: a Date table (or DateTime column joined to the fact table) with ≥ 4 periods → temporal (
scripts/propose_insights.py); otherwise categorical (scripts/propose_insights_categorical.py). - Audit all measures in ONE query — see recipe 2.0 in
references/powerbi-mcp-recipes.md(singleEVALUATE ROW(...)with every measure). Out-of-bounds results (% outside [-100,100], negative counts, blanks where data exists, NaN) becomebroken_measurecandidates. Never silently use a broken measure. - Run the minimal probe set (see recipes; batch aggressively — 3-4 MCP round-trips total is the norm).
There is no theme-extraction step. The render always uses the fixed built-in Power BI Motion palette — see "Visual style" below.
Read references/insight-detection.md before classifying; references/powerbi-mcp-recipes.md before querying.
Stage 2 — Propose (widget only)
Run the detector. Present at most 7 candidates as an interactive widget (Visualizer or host equivalent) — checkbox cards, never a prose/table list. Each card: class label, KPI + headline value, delta line (≤ 12 words), narration in quotes (≤ 14 words), confidence, rough duration. Default-check 2-3 that form a coherent narrative. Submit button sends the selected IDs via sendPrompt. Stop and wait; treat ambiguous replies ("looks good") as a request for confirmation, not approval.
Insight classes: references/insight-detection.md. Visual treatment per class: references/visual-grammar.md.
Stage 3 — Render
Two modes (ask if unclear; default Mode A for audience-facing output):
- Mode A — narrated report: one MP4; intro card → per-chapter title+setup cards → insight scenes → outro.
- Mode B — standalone clips: one MP4 per insight, no framing (same renderer, one chapter per config).
Steps:
- Build
report_config.json:{report:{title,subtitle,brand?}, outro:{recap}, source:{dataset,period}, chapters:[{title, setup, data:{insight:{class,narration}, ...}}]}. Nothemekey, nostarterkey — those no longer exist. See the class → payload contract below for whatdataneeds per class. - One command renders it:
python3 scripts/generate_video.py --config report_config.json --out renders/<slug>.mp4. That's the whole pipeline — no assemble step, no separate GIF step, no HyperFrames call. It validates the config up front and refuses to render with an actionable error rather than producing a blank or broken clip. present_filesthe single.mp4. Nothing else lands in the output directory — no companion GIF, HTML, or JSON. If you need the DAX audit trail, that's the config file already sitting in your own working directory; it is not a deliverable.
Visual style (fixed, not configurable)
Every render uses the same built-in Power BI Motion look — a deliberate simplification, not a missing feature:
- Palette: Power BI Desktop / Fluent UI defaults (blue
#118DFFprimary, coral#FD625Eaccent, teal#01B8AAsecondary series, near-black text, off-white canvas). Hardcoded ingenerate_video.py'sTHEMEdict. - Canvas is never pure white. It's Fluent's
#F3F2F1(Power BI Desktop's own canvas grey). This is a contrast-safety rule, not a cosmetic choice: a pure-white canvas plus any light-colored text element is a white-on-white bug waiting to happen. Off-white canvas + white card surfaces + dark/colored text is always safe. If you ever add a new painter, keep that invariant — never light-on-light. - Landscape, 1280×720, 24fps. Square format (the old default) cut off longer titles and member labels at the screen edges; 16:9 gives roughly double the horizontal wrap budget at the same font size. 24fps measured as the speed/smoothness sweet spot for this renderer (see
references/limitations.md). - Per-chapter duration is computed from on-screen text, not a flat constant — a chapter with four long member labels and a two-line narration holds longer than a one-line KPI callout. See
reading_seconds()in the script and "Reading-time model" inreferences/visual-grammar.md. Don't pass a manual duration override; fix the narration length instead if a chapter feels rushed or padded.
Insight class → payload contract
data.insight.class selects the painter, and each painter has a data contract. generate_video.py validates this before opening ffmpeg and refuses to render with an actionable error — but get it right at authoring time:
| Class family | Classes | Requires | Renders |
|---|---|---|---|
| Card grid | outlier, extreme_value, record, share_rarity, contribution | non-empty data.members ([{member, value or deltaPct, isOutlier}]) | KPI card grid, outlier gets a colored accent bar |
| Share bar | share_dominance, mix_shift, concentration | non-empty data.members ([{member, shareNow}]) | stacked bar |
| Time line | trend, anomaly, threshold_crossing | non-empty data.series ([{period, value}]) | line over ordered periods, with gridlines |
| Broken | broken_measure | data.measure | diagnosis card |
Choosing between them is a data-shape question, not a style question:
- Insight compares groups/segments (stance, accuracy brackets, regions…) → card grid family. This is the common case for categorical models.
- Insight moves along an ordered axis (months, quarters) → time line family.
- Never fabricate a time series to satisfy the time-line contract. If the dataset has no time axis (aggregate snapshots, one-row-per-entity), a "threshold" insight is really a group comparison: use
extreme_valuewith brackets as members. - Classes outside the table (e.g.
no_finding) are rejected, not silently defaulted. Reframe the insight into one of the shapes above — a flat "no effect" story fits the card grid with near-equal member values. - Optional
data.subtitleadds context to the card-grid header (e.g. "YoY change" — only when the delta really is year-over-year). No temporal wording is ever added automatically. outroat config top level:{"recap": "..."}(a bare string is accepted and auto-wrapped).
Hard rules
- Never render unapproved data. The proposal stage is non-optional.
- Animate the delta, not the value. No meaningful delta → not in the storyboard.
- Source attribution in every clip (bottom-left: dataset · period · Power BI Motion).
- No invented numbers. Failed/absurd DAX → surface and stop.
- One insight per clip.
- No prose deliverable — the widget is Stage 2, the MP4 is Stage 3.
- Only the
.mp4is a deliverable. Don't hand the user a GIF, HTML file, or JSON snapshot alongside it.
When NOT to use
Static chart wanted → Visualizer/matplotlib. Model modification → Power BI Modeling MCP directly. Report analysis/summary wanted → normal response, no pipeline. Real-time animation inside Power BI → out of scope.
What ships with it: 8 files
92.0 KB alongside SKILL.md, 3 of them executable
references/
- insight-detection.md5.3 KB
- limitations.md6.8 KB
- powerbi-mcp-recipes.md5.9 KB
- visual-grammar.md8.0 KB
scripts/
- generate_video.pyruns40.1 KB
- propose_insights_categorical.pyruns8.9 KB
- propose_insights.pyruns9.0 KB
- INSTALL.md7.9 KB