Remotion for Financial Data Visualization: Animate Portfolio Performance, Charts, and Earnings Reports
Remotion for Financial Data Visualization: Animate Portfolio Performance, Charts, and Earnings Reports
Static charts have dominated financial communications for decades — the bar chart in the PDF, the pie chart on the slide, the line graph embedded in the earnings release. These formats work, but they require viewers to do cognitive work: read the axes, scan the legend, mentally trace the trend. Motion graphics change that dynamic. An animated line chart drawing itself across the screen communicates trend direction intuitively. A number counting up to your fund’s AUM creates a visceral sense of scale. A pie chart that assembles slice by slice tells the asset allocation story rather than displaying it.
Remotion — videos built as React components — is a natural fit for financial data visualization. Financial data is structured and machine-readable. Remotion compositions accept typed props. The two connect directly: your data source (a JSON API, a spreadsheet export, a database query) feeds directly into the animation without a human in the loop. The result is repeatable, accurate, and scalable — quarterly reports that render themselves when the data is ready, factsheet videos that update automatically when portfolio weights change.
This guide covers the primary use cases in financial services, provides working code examples for the most common chart types, and addresses compliance considerations that apply specifically to video content in regulated contexts.
Use Cases in Financial Services
Portfolio Performance Animation
The classic performance chart — net asset value over time — is far more compelling in motion. Drawing the line from left to right across a fixed time axis, with the final value counting up in a large number display, turns a routine data point into a story: “Here is where we started, here is the journey, here is where we are.”
Firms use these for:
- Client report video supplements (emailed alongside the PDF)
- Advisor-facing materials that make performance tangible
- Social media clips (for firms permitted to share performance on social channels, with appropriate disclosures)
Market Data and Index Visualization
Real-time and historical market data — index levels, sector performance, yield curves — can be rendered into branded motion graphics for client communications, conference presentations, and media appearances. Because Remotion compositions are data-driven, you can point the same template at different index series and generate branded, consistently formatted clips for each.
Earnings Report Motion Graphics
Quarterly earnings presentations can incorporate animated highlights: revenue growth bars that fill to the reported number, EPS counters, year-over-year comparison animations. These are particularly effective as short video clips embedded in investor relations pages or sent to subscribers as video summaries before the full call.
Fund Factsheet Video
A fund factsheet video runs through the core data points from a monthly or quarterly factsheet — fund size, top holdings, geographic exposure, performance table — in a 60–90 second animated sequence. Asset managers use these as digital-native alternatives to PDF factsheets for platforms that favor video (social media, video email, digital client portals).
Stock Price Line Chart Animation
A smooth line chart that draws itself from left to right is one of the most immediately recognizable financial animations. The key is using SVG <path> with a strokeDashoffset technique driven by Remotion’s interpolate.
Data Structure
interface PricePoint {
date: string;
close: number;
}
interface LineChartProps {
data: PricePoint[];
lineColor: string;
labelColor: string;
animationDurationFrames: number;
}
Building the SVG Path
function buildLinePath(
data: PricePoint[],
width: number,
height: number,
padding: number
): string {
const prices = data.map((d) => d.close);
const minPrice = Math.min(...prices);
const maxPrice = Math.max(...prices);
const priceRange = maxPrice - minPrice;
const chartWidth = width - padding * 2;
const chartHeight = height - padding * 2;
const points = data.map((d, i) => {
const x = padding + (i / (data.length - 1)) * chartWidth;
const y = padding + chartHeight - ((d.close - minPrice) / priceRange) * chartHeight;
return `${x},${y}`;
});
return `M ${points.join(" L ")}`;
}
The Drawing Animation with strokeDashoffset
import { useCurrentFrame, interpolate } from "remotion";
const AnimatedLineChart: React.FC<LineChartProps> = ({
data,
lineColor,
animationDurationFrames,
}) => {
const frame = useCurrentFrame();
const WIDTH = 960;
const HEIGHT = 480;
const PADDING = 60;
const pathD = buildLinePath(data, WIDTH, HEIGHT, PADDING);
// We need the path length to set up the dash animation.
// For static compositions, pre-compute this; for dynamic, use a ref.
const PATH_LENGTH = 1200; // approximate; use SVGPathElement.getTotalLength() in real code
const drawProgress = interpolate(
frame,
[0, animationDurationFrames],
[PATH_LENGTH, 0],
{
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
}
);
return (
<svg
width={WIDTH}
height={HEIGHT}
style={{ overflow: "visible" }}
>
{/* Grid lines */}
{[0.25, 0.5, 0.75].map((ratio) => (
<line
key={ratio}
x1={PADDING}
y1={PADDING + (HEIGHT - PADDING * 2) * ratio}
x2={WIDTH - PADDING}
y2={PADDING + (HEIGHT - PADDING * 2) * ratio}
stroke="rgba(255,255,255,0.15)"
strokeWidth={1}
strokeDasharray="4 4"
/>
))}
{/* Animated price line */}
<path
d={pathD}
fill="none"
stroke={lineColor}
strokeWidth={3}
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray={PATH_LENGTH}
strokeDashoffset={drawProgress}
/>
{/* Gradient fill under the line */}
<defs>
<linearGradient id="areaGrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor={lineColor} stopOpacity={0.3} />
<stop offset="100%" stopColor={lineColor} stopOpacity={0} />
</linearGradient>
</defs>
</svg>
);
};
Getting the Exact Path Length
To animate strokeDashoffset correctly, you need the SVG path’s getTotalLength(). In a Remotion composition, the safest approach is to compute it at build time and pass it as a prop:
// In a Node.js script that prepares render data:
import { JSDOM } from "jsdom";
function computePathLength(pathD: string): number {
const dom = new JSDOM(`<svg><path id="p" d="${pathD}" /></svg>`);
const path = dom.window.document.getElementById("p") as SVGPathElement;
return path.getTotalLength();
}
Pass pathLength as a prop to your composition, and use it as the strokeDasharray value.
Candlestick Chart Animation
Candlestick charts are more complex than line charts because each candle is a compound visual element: the body (open–close range), the wicks (high–low range), and a color (green for up days, red for down days).
Data Structure
interface Candle {
date: string;
open: number;
high: number;
low: number;
close: number;
}
Rendering Candles with Staggered Entry
const CandlestickChart: React.FC<{ candles: Candle[] }> = ({ candles }) => {
const frame = useCurrentFrame();
const WIDTH = 960;
const HEIGHT = 480;
const PADDING = 60;
const candleWidth = Math.floor((WIDTH - PADDING * 2) / candles.length) - 2;
const allPrices = candles.flatMap((c) => [c.high, c.low]);
const minPrice = Math.min(...allPrices);
const maxPrice = Math.max(...allPrices);
const range = maxPrice - minPrice;
const toY = (price: number) =>
PADDING + (HEIGHT - PADDING * 2) * (1 - (price - minPrice) / range);
// Each candle enters sequentially, staggered by 3 frames
const framesPerCandle = 3;
return (
<svg width={WIDTH} height={HEIGHT}>
{candles.map((candle, i) => {
const candleFrame = frame - i * framesPerCandle;
const opacity = interpolate(candleFrame, [0, 8], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
const scaleY = interpolate(candleFrame, [0, 10], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
const x = PADDING + i * (candleWidth + 2) + candleWidth / 2;
const bodyTop = toY(Math.max(candle.open, candle.close));
const bodyBottom = toY(Math.min(candle.open, candle.close));
const bodyHeight = Math.max(bodyBottom - bodyTop, 1);
const isUp = candle.close >= candle.open;
const color = isUp ? "#22C55E" : "#EF4444";
return (
<g key={i} opacity={opacity} transform={`scale(1, ${scaleY})`} style={{ transformOrigin: `${x}px ${HEIGHT / 2}px` }}>
{/* Wick */}
<line
x1={x} y1={toY(candle.high)}
x2={x} y2={toY(candle.low)}
stroke={color} strokeWidth={1.5}
/>
{/* Body */}
<rect
x={x - candleWidth / 2}
y={bodyTop}
width={candleWidth}
height={bodyHeight}
fill={color}
rx={1}
/>
</g>
);
})}
</svg>
);
};
Animated Pie and Donut Chart for Asset Allocation
Asset allocation is a natural fit for pie/donut charts. The animation that works best is sector-by-sector assembly — each slice of the pie draws in sequentially, accompanied by a label fade-in.
SVG Arc-Based Pie Animation
interface Slice {
label: string;
value: number;
color: string;
}
function describeArc(cx: number, cy: number, r: number, startAngle: number, endAngle: number): string {
const start = polarToCartesian(cx, cy, r, endAngle);
const end = polarToCartesian(cx, cy, r, startAngle);
const largeArc = endAngle - startAngle <= 180 ? "0" : "1";
return `M ${cx} ${cy} L ${start.x} ${start.y} A ${r} ${r} 0 ${largeArc} 0 ${end.x} ${end.y} Z`;
}
function polarToCartesian(cx: number, cy: number, r: number, angle: number) {
const rad = ((angle - 90) * Math.PI) / 180;
return { x: cx + r * Math.cos(rad), y: cy + r * Math.sin(rad) };
}
const DonutChart: React.FC<{ slices: Slice[]; innerRadius: number }> = ({ slices, innerRadius }) => {
const frame = useCurrentFrame();
const total = slices.reduce((sum, s) => sum + s.value, 0);
const cx = 300, cy = 300, r = 220;
let currentAngle = 0;
return (
<svg width={600} height={600}>
<defs>
<circle id="inner" cx={cx} cy={cy} r={innerRadius} />
</defs>
{slices.map((slice, i) => {
const sliceAngle = (slice.value / total) * 360;
const startAngle = currentAngle;
const endAngle = currentAngle + sliceAngle;
currentAngle += sliceAngle;
const entryDelay = i * 12;
const animatedEnd = interpolate(
frame - entryDelay,
[0, 20],
[startAngle, endAngle],
{ extrapolateLeft: "clamp", extrapolateRight: "clamp" }
);
const opacity = interpolate(frame - entryDelay, [0, 10], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
return (
<g key={i} opacity={opacity}>
<path
d={describeArc(cx, cy, r, startAngle, animatedEnd)}
fill={slice.color}
/>
</g>
);
})}
{/* Donut hole */}
<circle cx={cx} cy={cy} r={innerRadius} fill="#0F172A" />
</svg>
);
};
Number Counter for AUM and Returns
The animated number counter is among the most versatile components in financial video. It works for AUM, fund returns, share price, percentage change, or any scalar value.
interface CounterProps {
from: number;
to: number;
prefix?: string;
suffix?: string;
decimals?: number;
animationFrames: number;
style?: React.CSSProperties;
}
const AnimatedCounter: React.FC<CounterProps> = ({
from,
to,
prefix = "",
suffix = "",
decimals = 0,
animationFrames,
style,
}) => {
const frame = useCurrentFrame();
const value = interpolate(frame, [0, animationFrames], [from, to], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
easing: Easing.out(Easing.cubic),
});
const formatted = value.toLocaleString("en-US", {
minimumFractionDigits: decimals,
maximumFractionDigits: decimals,
});
return (
<div style={{
fontFamily: '-apple-system, "Segoe UI", Roboto, sans-serif',
fontWeight: 700,
fontVariantNumeric: "tabular-nums",
...style,
}}>
{prefix}{formatted}{suffix}
</div>
);
};
// Usage examples
<AnimatedCounter from={0} to={4700000000} prefix="$" suffix=" AUM" decimals={0} animationFrames={60} style={{ fontSize: 96, color: "#F8FAFC" }} />
<AnimatedCounter from={0} to={18.7} suffix="%" decimals={1} animationFrames={45} style={{ fontSize: 72, color: "#22C55E" }} />
Quarterly Report Automation
The highest-leverage application of Remotion in financial services is automating the production of quarterly video summaries. The workflow:
- Data preparation — A script pulls the quarter’s key metrics from your data warehouse or reporting system into a structured JSON object.
- Template rendering —
@remotion/lambdarenders the JSON data through a pre-designed quarterly report composition. - Review and compliance sign-off — An internal reviewer checks the rendered video before distribution.
- Distribution — The approved video is posted to the investor portal, distributed via email, and published to relevant social channels.
Steps 1, 2, and 4 require zero human involvement once the pipeline is established. Step 3 is the appropriate human checkpoint for compliance purposes.
Example Input Props for a Quarterly Report Composition
interface QuarterlyReportProps {
fundName: string;
quarter: string; // e.g., "Q2 2026"
navPerShare: number;
quarterlyReturn: number; // percentage
ytdReturn: number; // percentage
inceptionReturn: number; // percentage
aumMillions: number;
topHoldings: Array<{ name: string; weight: number }>;
benchmarkName: string;
benchmarkReturn: number;
disclosureText: string;
}
Compliance Considerations for Financial Video Content
Financial video content is subject to the same regulatory requirements as any other marketing communication. Key considerations:
1. Performance advertising rules In most jurisdictions (US: SEC/FINRA; UK: FCA; EU: MiFID II), advertising past performance requires specific disclosures. The canonical disclaimer — “past performance is not indicative of future results” — must be present and legible. In video, this typically means: 18–20pt minimum font, displayed for at least 3–5 seconds, not overlaid on competing visual content.
2. Fair and balanced presentation Regulators require that performance be presented for a time period that is not selectively chosen to flatter the fund. Showing only a bull-market period without context may constitute a misleading communication.
3. Approval workflows
Most regulated firms require compliance pre-approval of marketing communications. Design your Remotion pipeline with a staging step: render to an internal review URL before the video is published. The @remotion/lambda output S3 URL can be shared with compliance reviewers via a simple internal tool.
4. Archiving FINRA Rule 4511 (US) and MiFID II (EU) require firms to retain copies of all marketing communications. Ensure your rendering pipeline logs the output S3 URL and the input data that generated each video.
5. Disclosure automation Pass legal disclosure text as a prop to your composition. This ensures the correct, current disclosure language appears in every rendered video — a single-source update to the disclosure prop updates all future renders automatically.
RenderComp Financial Visualization Templates
RenderComp offers a set of Remotion templates built specifically for financial data visualization, including:
- Animated line chart component with area fill and gradient styling
- Candlestick chart with configurable color schemes and staggered entry animation
- Donut and pie chart for asset allocation with label animations
- Number counter component supporting currency, percentage, and large number formatting
- Quarterly report composition with configurable sections and disclaimer display
- Lambda-ready render scripts with structured JSON input
All templates are TypeScript-strict, free of hardcoded values, and documented for financial services teams. Visit rendercomp.com to browse the catalog.
FAQ
Q: Does Remotion support real-time data updates — can I render a video that reflects live market data?
A: Remotion renders video files, not live streams. However, you can automate renders that are triggered when data changes. A webhook from your data pipeline can trigger a @remotion/lambda render whenever new data is available, producing a fresh video within minutes. For truly live displays (tickers, live charts), a React-based web component is more appropriate than a video.
Q: How do I get the correct SVG path length for the line chart drawing animation?
A: Use SVGPathElement.getTotalLength() in a browser or JSDOM environment. In a Remotion project, run a Node.js pre-processing script that computes path lengths for each data series and passes them as props to the composition. Alternatively, use a generous overestimate — a slightly longer strokeDasharray than the actual path length will not affect the visual result for the strokeDashoffset animation.
Q: Can Remotion render financial charts in HD for use in broadcast or investor conference presentations? A: Yes. Remotion renders at any resolution specified in the composition. For broadcast or conference use, 1920×1080 (Full HD) or 3840×2160 (4K UHD) are both supported. Higher resolutions require more memory and longer render times — scale up Lambda function memory allocation accordingly.
Q: How should I handle negative returns in an animated counter or bar chart? A: Design your color system to distinguish positive and negative values explicitly. For counters, conditionally apply red for negative values and green for positive. For bar charts, render bars below a baseline axis for negative values, using the same axis line as both the zero reference and the visual origin point.
Q: Is Remotion suitable for rendering charts that need to show uncertainty ranges or confidence intervals?
A: Yes. SVG paths can describe any shape, including shaded confidence bands. Render the band as a filled <path> using the upper and lower bounds of your interval, with low opacity fill (e.g., opacity: 0.2), behind the main data line. Animate the band in sync with the main chart line using the same strokeDashoffset or opacity timing.
Q: What is the best way to handle very large datasets (thousands of data points) in a Remotion composition? A: Pre-downsample the data before passing it as props. For a 90-second chart animation at 30 fps, 300 data points is more than sufficient visual resolution for a smooth line. Compute a downsampled array in your data preparation script (using LTTB — Largest Triangle Three Buckets — algorithm for visually faithful downsampling) and pass the reduced array as the composition’s input props.
Q: How do I ensure the disclosure text in my financial video is large enough to comply with regulations?
A: At 1920×1080, 18pt at 96dpi corresponds to approximately 24px. At 3840×2160 (4K), double that. Define a minimum font size constant in your composition and enforce it via a TypeScript check in your render script. Display the disclosure for a minimum of 3 seconds — at 30 fps, that is 90 frames. Use <Series> to guarantee the disclosure segment is always at least that duration regardless of content length.
Now available
Get 1,000+ Remotion Templates
Pay once — no subscription. Lifetime updates. TypeScript-first.
View pricing →