Basic Streamline Plot — Altair

A streamline plot visualizes vector fields using smooth curves that are tangent to the field at every point. Unlike quiver plots that show discrete arrows, streamlines trace continuous paths through the field, revealing flow patterns, circulation, and field topology. This visualization is ideal for understanding fluid dynamics, electromagnetic fields, or gradient fields where the continuous nature of the flow is important.

Basic Streamline Plot rendered with Altair

Python source (Altair)

""" anyplot.ai
streamline-basic: Basic Streamline Plot
Library: altair 6.1.0 | Python 3.13.13
Quality: 88/100 | Updated: 2026-05-14
"""

import os

import altair as alt
import numpy as np
import pandas as pd


# Theme tokens
THEME = os.getenv("ANYPLOT_THEME", "light")
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"

# Disable data row limit
alt.data_transformers.disable_max_rows()

# Data - Create a vector field for a vortex flow (u = -y, v = x)
np.random.seed(42)

# Generate streamlines using Euler integration
streamlines_data = []
streamline_id = 0

# Starting points at different radii for vortex visualization
radii = [0.4, 0.7, 1.0, 1.4, 1.8, 2.2, 2.6, 3.0]
n_per_radius = 6
dt = 0.03
max_steps = 250

for r in radii:
    for i in range(n_per_radius):
        angle = 2 * np.pi * i / n_per_radius + (r * 0.1)
        x = r * np.cos(angle)
        y = r * np.sin(angle)
        points = [(x, y)]

        # Trace streamline using Euler integration
        for _ in range(max_steps):
            # Vector field: circular vortex (u = -y, v = x)
            u = -y
            v = x
            mag = np.sqrt(u**2 + v**2)
            if mag < 1e-6:
                break
            # Normalize and step
            x_new = x + dt * u / mag
            y_new = y + dt * v / mag
            # Stop if out of bounds
            if abs(x_new) > 3.2 or abs(y_new) > 3.2:
                break
            x, y = x_new, y_new
            points.append((x, y))

        # Only include streamlines with enough points
        if len(points) > 5:
            for j, (px, py) in enumerate(points):
                # Velocity magnitude equals distance from center in this vortex
                vel = np.sqrt(px**2 + py**2)
                streamlines_data.append(
                    {"x": float(px), "y": float(py), "streamline_id": streamline_id, "order": j, "velocity": float(vel)}
                )
            streamline_id += 1

df = pd.DataFrame(streamlines_data)

# Compute average velocity per streamline for color encoding
avg_velocity = df.groupby("streamline_id")["velocity"].mean().reset_index()
avg_velocity.columns = ["streamline_id", "avg_velocity"]
df = df.merge(avg_velocity, on="streamline_id")

# Create the streamline chart using line marks
chart = (
    alt.Chart(df)
    .mark_line(strokeWidth=2.5, opacity=0.85)
    .encode(
        x=alt.X("x:Q", title="X Position (units)", scale=alt.Scale(domain=[-3.5, 3.5])),
        y=alt.Y("y:Q", title="Y Position (units)", scale=alt.Scale(domain=[-3.5, 3.5])),
        color=alt.Color(
            "avg_velocity:Q",
            scale=alt.Scale(scheme="viridis"),
            title="Flow Speed",
            legend=alt.Legend(titleFontSize=18, labelFontSize=16, gradientLength=200),
        ),
        detail="streamline_id:N",
        order="order:O",
    )
    .properties(
        width=1600,
        height=900,
        title=alt.Title("streamline-basic · altair · anyplot.ai", fontSize=28, anchor="middle"),
        background=PAGE_BG,
    )
    .configure_view(fill=PAGE_BG, stroke=INK_SOFT)
    .configure_axis(
        domainColor=INK_SOFT,
        tickColor=INK_SOFT,
        gridColor=INK,
        gridOpacity=0.10,
        labelColor=INK_SOFT,
        titleColor=INK,
        labelFontSize=18,
        titleFontSize=22,
    )
    .configure_title(color=INK, fontSize=28)
    .configure_legend(fillColor=ELEVATED_BG, strokeColor=INK_SOFT, labelColor=INK_SOFT, titleColor=INK)
)

# Save as PNG and HTML
chart.save(f"plot-{THEME}.png", scale_factor=3.0)
chart.save(f"plot-{THEME}.html")

Part of Basic Streamline Plot on anyplot.ai.

Other implementations