agentsclimarketplace

Remotion data viz

Skill phamthanhnghia/remotion-agent-skills/skills/remotion-data-viz

Data visualization and infographics for Remotion. Use for animated charts, counters, progress bars, SVG paths, recharts, and D3 integrations.From its SKILL.md

Install
npx -y skills add phamthanhnghia/remotion-agent-skills --skill remotion-data-viz

Assembled 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.
  • 1 stars1 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

6.7 KB, ~1.8k tokens by cl100k_base, as published. Nobody here has run it

Remotion Data Viz

Animated data visualizations as Remotion compositions — frame-deterministic, React-based, seekable.

For the composition contract see remotion-core. For animation primitives see remotion-animation.

Core principle

Data visualizations in Remotion are animated from frame 0 to durationInFrames. All chart values are derived from useCurrentFrame() — there is no re-render on resize or data change, only a deterministic mapping of frame → visual state.

Common patterns

Animated counter

import {useCurrentFrame, useVideoConfig, interpolate} from 'remotion';

type CounterProps = {
  from: number;
  to: number;
  durationInFrames: number;
  prefix?: string;
  suffix?: string;
};

const AnimatedCounter: React.FC<CounterProps> = ({from, to, durationInFrames, prefix = '', suffix = ''}) => {
  const frame = useCurrentFrame();
  const value = interpolate(frame, [0, durationInFrames], [from, to], {
    extrapolateRight: 'clamp',
    easing: (t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t, // ease-in-out-quad
  });

  return (
    <div style={{fontSize: 120, fontWeight: 700, color: '#fff', fontVariantNumeric: 'tabular-nums'}}>
      {prefix}{Math.round(value).toLocaleString()}{suffix}
    </div>
  );
};

Bar chart reveal

import {interpolate, useCurrentFrame} from 'remotion';

type BarProps = {label: string; value: number; maxValue: number; index: number; color: string};

const AnimatedBar: React.FC<BarProps> = ({label, value, maxValue, index, color}) => {
  const frame = useCurrentFrame();
  const STAGGER = 5;
  const DURATION = 40;

  const progress = interpolate(
    frame,
    [index * STAGGER, index * STAGGER + DURATION],
    [0, 1],
    {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'}
  );

  const barWidth = (value / maxValue) * 600 * progress;

  return (
    <div style={{marginBottom: 20}}>
      <div style={{color: '#ccc', fontSize: 24, marginBottom: 8}}>{label}</div>
      <div style={{height: 48, backgroundColor: '#1e1e2e', borderRadius: 8, overflow: 'hidden'}}>
        <div
          style={{
            height: '100%',
            width: barWidth,
            backgroundColor: color,
            borderRadius: 8,
            display: 'flex',
            alignItems: 'center',
            paddingLeft: 16,
          }}
        >
          <span style={{color: 'white', fontWeight: 700}}>{Math.round(value * progress).toLocaleString()}</span>
        </div>
      </div>
    </div>
  );
};

Line chart draw (SVG)

import {interpolate, useCurrentFrame} from 'remotion';

const LineChart: React.FC<{data: number[]; width: number; height: number}> = ({data, width, height}) => {
  const frame = useCurrentFrame();
  const {durationInFrames} = useVideoConfig();

  const progress = interpolate(frame, [0, durationInFrames * 0.8], [0, 1], {extrapolateRight: 'clamp'});

  const maxVal = Math.max(...data);
  const points = data
    .slice(0, Math.ceil(data.length * progress))
    .map((v, i) => `${(i / (data.length - 1)) * width},${height - (v / maxVal) * height}`)
    .join(' ');

  return (
    <svg width={width} height={height}>
      <polyline
        points={points}
        fill="none"
        stroke="#6366f1"
        strokeWidth={4}
        strokeLinecap="round"
        strokeLinejoin="round"
      />
    </svg>
  );
};

Pie / donut reveal

const DonutChart: React.FC<{segments: {value: number; color: string}[]; size: number}> = ({segments, size}) => {
  const frame = useCurrentFrame();
  const {durationInFrames} = useVideoConfig();

  const progress = interpolate(frame, [0, durationInFrames * 0.7], [0, 1], {extrapolateRight: 'clamp'});
  const radius = size / 2 - 20;
  const cx = size / 2;
  const cy = size / 2;
  const total = segments.reduce((s, seg) => s + seg.value, 0);

  let cumulative = 0;
  const paths = segments.map((seg, i) => {
    const startAngle = (cumulative / total) * 2 * Math.PI - Math.PI / 2;
    cumulative += seg.value;
    const endAngle = (cumulative / total) * 2 * Math.PI * progress - Math.PI / 2;

    const x1 = cx + radius * Math.cos(startAngle);
    const y1 = cy + radius * Math.sin(startAngle);
    const x2 = cx + radius * Math.cos(endAngle);
    const y2 = cy + radius * Math.sin(endAngle);
    const largeArc = endAngle - startAngle > Math.PI ? 1 : 0;

    return (
      <path
        key={i}
        d={`M ${cx} ${cy} L ${x1} ${y1} A ${radius} ${radius} 0 ${largeArc} 1 ${x2} ${y2} Z`}
        fill={seg.color}
      />
    );
  });

  return <svg width={size} height={size}>{paths}</svg>;
};

Using recharts

npm install recharts

Important: recharts uses requestAnimationFrame and DOM measurements internally. Use spring() and interpolate() to compute chart data values before passing to recharts — do not rely on recharts' internal animation (it's not frame-deterministic).

import {BarChart, Bar, XAxis, YAxis} from 'recharts';

const MyChart: React.FC = () => {
  const frame = useCurrentFrame();
  const progress = interpolate(frame, [0, 60], [0, 1], {extrapolateRight: 'clamp'});

  const data = [
    {name: 'A', value: Math.round(1200 * progress)},
    {name: 'B', value: Math.round(800 * progress)},
    {name: 'C', value: Math.round(1600 * progress)},
  ];

  return (
    <BarChart width={600} height={400} data={data}>
      <XAxis dataKey="name" />
      <YAxis />
      <Bar dataKey="value" fill="#6366f1" isAnimationActive={false} />
    </BarChart>
  );
};

Always set isAnimationActive={false} on recharts components — their internal animation is not frame-deterministic.

calculateMetadata for data-driven compositions

When the composition duration depends on the data length, use calculateMetadata:

import {Composition} from 'remotion';

type DataVizProps = {data: {label: string; value: number}[]};

const dataViz = {
  component: DataVizScene,
  calculateMetadata: async ({props}: {props: DataVizProps}) => {
    const fps = 30;
    const framesPerBar = 20;
    const durationInFrames = props.data.length * framesPerBar + 60; // padding
    return {durationInFrames, fps};
  },
};

References

TaskRead
Animated counter, bar chart, line chart, pie patternsreferences/chart-patterns.md
recharts integration (isAnimationActive, data binding)references/recharts.md
D3 calculations (scale, path) with React renderingreferences/d3-in-remotion.md
calculateMetadata for dynamic durationremotion-core/references/calculate-metadata.md
spring/interpolate for data revealsremotion-keyframes

What ships with it: 1 file

4.6 KB alongside SKILL.md

references/

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.