TerminalSkills/skills

plotly

Plotly is an open-source charting library that draws interactive charts (hover, zoom, pan, export) from Python with plotly.py and Plotly Express, or in the browser with plotly.js.

View source

How to use

  1. Copy the installation command and review the source package before installing it in your own agent environment.
  2. Check the source document for required tools, credentials and supported hosts.
  3. Describe your input and desired deliverable; review the output against the source material.
npx skills add https://github.com/TerminalSkills/skills --skill plotly

Before you start

  • Installation does not include a model subscription, API allowance or permission to access your files.
  • Repository stars and installation counts describe activity, not an independent quality or security test.
Original skill document

Rendered from the source repository. Headings, examples, code, tables, links, and referenced images are preserved.

Plotly — Interactive Scientific Visualization

Overview

Plotly is an interactive charting library for Python and JavaScript. plotly.py builds a figure (a JSON description of traces and layout) and plotly.js renders it in the browser with hover tooltips, zoom and export; Plotly Express is the one-call API on top, Dash turns figures into web dashboards, and react-plotly.js wraps plotly.js for React. This skill follows plotly.py 7.1 (which bundles plotly.js 4.1), Dash 4.4 and react-plotly.js 4.1.

Instructions

Python (Plotly Express)

python
import plotly.express as px

# Scatter plot with color and size encoding
gap = px.data.gapminder().query("year == 2007")
fig = px.scatter(
    gap, x="gdpPercap", y="lifeExp",
    size="pop", color="continent",
    hover_name="country",
    log_x=True,
    size_max=60,
    title="GDP vs Life Expectancy (2007)"
)
fig.show()

# Time series with multiple lines
stocks = px.data.stocks()
fig = px.line(stocks, x="date", y=["GOOG", "AAPL", "AMZN", "FB", "MSFT"],
              title="Stock Prices Over Time")
fig.update_layout(yaxis_title="Price ($)", legend_title="Company")

# Heatmap of a correlation matrix
corr = px.data.iris().drop(columns=["species", "species_id"]).corr()
fig = px.imshow(corr, text_auto=".2f", color_continuous_scale="RdBu_r",
                title="Feature Correlation Matrix")

# Bar chart of totals per category: px.histogram sums the rows (px.bar draws one bar per row, so aggregate first)
tips = px.data.tips()
fig = px.histogram(tips, x="day", y="total_bill", histfunc="sum", color="sex", barmode="group",
                   category_orders={"day": ["Thur", "Fri", "Sat", "Sun"]})

# Geographic choropleth
fig = px.choropleth(
    gap, locations="iso_alpha", color="gdpPercap",
    hover_name="country",
    color_continuous_scale="Viridis",
    title="GDP Per Capita by Country"
)

Plotly Express accepts pandas, Polars and PyArrow data frames directly (since 6.0, through Narwhals), as well as dicts and lists.

Tile maps use px.scatter_map, px.line_map, px.density_map and px.choropleth_map (MapLibre, no access token). The *_mapbox functions and go.Scattermapbox were removed in 7.0.

Subplots and graph objects

Non-cartesian traces (indicator, pie, 3D, maps) need a matching entry in specs; without it add_trace raises ValueError: Trace type 'indicator' is not compatible with subplot type 'xy'.

python
from plotly.subplots import make_subplots
import plotly.graph_objects as go

months = ["2026-01", "2026-02", "2026-03", "2026-04", "2026-05", "2026-06"]
revenue = [41200, 43850, 47310, 45120, 52480, 58930]
users = [1830, 1912, 2054, 2101, 2260, 2415]
churn = [2.9, 2.7, 2.8, 2.4, 2.2, 2.1]

fig = make_subplots(
    rows=2, cols=2,
    specs=[[{"type": "xy"}, {"type": "xy"}], [{"type": "xy"}, {"type": "indicator"}]],
    subplot_titles=("Revenue", "Users", "Churn %", ""),
)
fig.add_trace(go.Scatter(x=months, y=revenue, mode="lines+markers"), row=1, col=1)
fig.add_trace(go.Scatter(x=months, y=users, mode="lines"), row=1, col=2)
fig.add_trace(go.Scatter(x=months, y=churn, fill="tozeroy"), row=2, col=1)
fig.add_trace(go.Indicator(mode="gauge+number", value=72, title={"text": "NPS"},
                           gauge={"axis": {"range": [0, 100]}}), row=2, col=2)
fig.update_layout(height=600, showlegend=False)

Export

python
config = {"showSendToCloud": False, "displaylogo": False}

fig.show(config=config)                                  # browser or notebook
fig.write_html("report.html", include_plotlyjs="cdn", config=config)   # ~10 KB + CDN script
fig.write_html("offline.html", config=config)            # self-contained, embeds ~4.8 MB of plotly.js
fig.write_image("chart.png", width=1000, height=500, scale=2)          # also .svg, .pdf, .jpeg, .webp

write_image needs Kaleido 1.x and a Chrome or Chromium install. If none is found, run plotly_get_chrome (or plotly.io.get_chrome()). The engine= argument and Orca support were removed in 7.0. plotly.io.write_images([...], [...]) exports many figures faster than a loop.

JavaScript (Plotly.js)

Since plotly.js 3, titles must be objects: title: "Monthly Revenue" is ignored without an error.

typescript
import Plotly from "plotly.js-dist-min";

const months = ["2026-01-01", "2026-02-01", "2026-03-01", "2026-04-01", "2026-05-01", "2026-06-01"];
const revenue = [41200, 43850, 47310, 45120, 52480, 58930];

// Renders into the element with id="revenue-chart"
Plotly.newPlot("revenue-chart", [
  {
    x: months,
    y: revenue,
    type: "scatter",
    mode: "lines+markers",
    name: "Revenue",
    line: { color: "#4f46e5", width: 2 },
    hovertemplate: "%{x|%b %Y}<br>$%{y:,.0f}<extra></extra>",
  },
], {
  title: { text: "Monthly Revenue" },
  xaxis: { title: { text: "Month" } },
  yaxis: { title: { text: "Revenue ($)" }, tickformat: "$,.0f" },
  hovermode: "x unified",
}, { responsive: true, displaylogo: false, showSendToCloud: false });

// Update in place: pass the complete new traces and layout — anything left out (axis titles, colors) reverts to defaults
Plotly.react("revenue-chart", [{ x: months, y: revenue.map((v) => v * 1.1) }], { title: { text: "Forecast" } });

React (react-plotly.js)

The default export needs the full plotly.js package as a peer dependency. To use a prebuilt or partial bundle instead, build the component with the factory (npm install react-plotly.js plotly.js-basic-dist-min; the partial bundles ship no type declarations, so TypeScript also needs npm install -D @types/plotly.js-basic-dist-min):

tsx
import Plotly from "plotly.js-basic-dist-min";   // partial bundle: scatter, bar and pie only, ~1.2 MB
import createPlotlyComponent from "react-plotly.js/factory";

const Plot = createPlotlyComponent(Plotly);

export function RevenueChart({ months, revenue }: { months: string[]; revenue: number[] }) {
  return (
    <Plot
      data={[{ x: months, y: revenue, type: "scatter", mode: "lines+markers" }]}
      layout={{ autosize: true, title: { text: "Monthly Revenue" } }}
      config={{ displaylogo: false, showSendToCloud: false }}
      style={{ width: "100%", height: "360px" }}
      useResizeHandler
    />
  );
}

The plot redraws only when data, layout or config change identity, so create new arrays and objects instead of mutating them. plotly.js needs a browser: importing it during server-side rendering throws ReferenceError: self is not defined, so in Next.js load this component with next/dynamic and ssr: false.

Dash (Python Web Framework)

python
# app.py — interactive dashboard with Plotly + Dash
from dash import Dash, html, dcc, callback, Output, Input
import plotly.express as px

df = px.data.gapminder()
continents = sorted(df["continent"].unique())

app = Dash(__name__)

app.layout = html.Div([
    html.H1("Life expectancy by continent"),
    dcc.Dropdown(id="continent", options=continents, value="Europe", clearable=False),
    dcc.Graph(id="life-exp", config={"showSendToCloud": False}),
])

@callback(Output("life-exp", "figure"), Input("continent", "value"))
def update_chart(continent):
    subset = df[df["continent"] == continent]
    return px.line(subset, x="year", y="lifeExp", color="country",
                   title=f"Life expectancy in {continent}")

if __name__ == "__main__":
    app.run(debug=True)   # http://127.0.0.1:8050 — app.run_server() was removed in Dash 3

Installation

bash
pip install "plotly[express]" pandas      # plotly.py + numpy for Plotly Express
pip install kaleido                        # static image export (needs Chrome: plotly_get_chrome)
pip install dash                           # Dash framework
npm install plotly.js-dist-min             # JavaScript, full minified bundle
npm install react-plotly.js plotly.js      # React wrapper + its peer dependency

Examples

Example 1: Chart for a report, as HTML and PNG

User request: "Plot monthly revenue from revenue.csv and give me an interactive HTML file plus a PNG for the slide deck."

python
# revenue_chart.py — columns in revenue.csv: month (YYYY-MM-DD), revenue
import pandas as pd
import plotly.express as px

sales = pd.read_csv("revenue.csv", parse_dates=["month"])

fig = px.line(sales, x="month", y="revenue", markers=True, title="Monthly revenue")
fig.update_traces(hovertemplate="%{x|%b %Y}<br>$%{y:,.0f}<extra></extra>")
fig.update_layout(hovermode="x unified", yaxis_tickprefix="$", yaxis_tickformat=",.0f")

fig.write_html("revenue.html", include_plotlyjs="cdn", config={"showSendToCloud": False})
fig.write_image("revenue.png", width=1000, height=500, scale=2)
bash
pip install "plotly[express]" pandas kaleido
python revenue_chart.py

Result: revenue.html (about 8 KB; it loads plotly.js from cdn.plot.ly, so it needs network access when opened) and revenue.png at 2000×1000 px. On a machine without Chrome the write_image line fails with "Kaleido requires Google Chrome to be installed"; plotly_get_chrome -y fixes that.

Example 2: Upgrade a plotly.js 2 chart that lost its titles

User request: "After updating plotly.js our chart has no title or axis labels and there is a new Share button in the toolbar."

diff
 Plotly.newPlot("signups-chart", traces, {
-  title: "Weekly signups",
-  xaxis: { title: "Week", titlefont: { size: 12 } },
-  yaxis: { title: "Signups" },
-});
+  title: { text: "Weekly signups" },
+  xaxis: { title: { text: "Week", font: { size: 12 } } },
+  yaxis: { title: { text: "Signups" } },
+}, { showSendToCloud: false });

Check any figure before shipping with Plotly.validate, which lists every key the current version rejects:

javascript
console.log(Plotly.validate(traces, layout));
// [{ code: "object", msg: "In layout, key title must be linked to an object container", ... }]

After the change the title and both axis labels render again and the Share button is gone from the modebar. If the chart used scattermapbox traces, rename them to scattermap and the mapbox layout key to map in the same pass.

Guidelines

  1. Plotly Express first — px.scatter, px.line, px.bar, px.histogram and friends cover most charts; drop to go.Figure and add_trace for mixed trace types or fine control. Every px call returns a normal go.Figure you can keep editing with update_layout and update_traces.
  2. Hover templates — %{x}, %{y}, %{customdata[0]} with d3 formats (%{y:,.0f}, %{x|%b %Y}); end with <extra></extra> to hide the trace-name box.
  3. The Share button uploads data — plotly.js 4 shows a "Share chart" modebar button by default that sends the figure to Plotly Cloud after a confirmation. Set showSendToCloud: False in config for internal or sensitive data; this applies to fig.show, write_html, dcc.Graph(config=...) and Plotly.newPlot.
  4. Static export depends on Chrome — Kaleido 1.x no longer ships a browser. In containers and CI, install Chromium or run plotly_get_chrome -y in the image build, not at request time.
  5. Bundle size — the full plotly.js-dist-min is about 4.8 MB minified (1.5 MB gzipped). When you only need a few trace types use a partial bundle: plotly.js-basic-dist-min (scatter, bar, pie; ~1.2 MB), -cartesian-, -geo-, -gl3d-, -finance- or -map-.
  6. Colors in plotly.js 4 — rgb(0.2, 0.4, 0.6) is no longer read as fractions of 255 (it now renders almost black) and hsv() strings are rejected; use 0–255 values, hex or hsl().
  7. Large data — SVG rendering slows down past a few thousand points. Plotly Express picks WebGL on its own (render_mode="auto"); with graph objects or plotly.js use go.Scattergl / type: "scattergl", or aggregate before plotting.
  8. Notebooks — plotly.py 6+ needs Jupyter Notebook 7+ or JupyterLab; go.FigureWidget also needs pip install anywidget.
  9. `app.run(debug=True)` is for development — the Dash debug mode enables a code-reloading dev server; deploy with a WSGI server such as gunicorn app:server after exposing server = app.server.
  10. 3D sparingly — 3D charts are hard to read; use 2D unless the third dimension adds real insight.
from this repository

More skills

All skills
TerminalSkills
Community

3dsmax-rendering

Scripts and configures production rendering in Autodesk 3ds Max with the V-Ray and Corona renderers: output size and files, render elements, denoising, light mix, batch and command-line rendering, and network rendering. Use when a user asks to set up a production render, render several cameras in one batch, render from the command line or on a render farm, add render passes for compositing, or cut render time for archviz and product shots.

installs
0
GitHub stars
180
Updated
Oct 25
TerminalSkills
Community

3dsmax-scripting

Covers scripting Autodesk 3ds Max, the 3D modeling and rendering application, with MAXScript and Python (pymxs): scene manipulation, object creation, material assignment, camera and light setup, batch operations, and file I/O. Use when tasks involve automating repetitive 3ds Max workflows, batch processing scenes, running scripts headless with 3dsmaxbatch, creating custom tools, or scripting scene setup for archviz, product visualization, or VFX.

installs
0
GitHub stars
180
Updated
Oct 25
TerminalSkills
Community

3proxy

3proxy is a small open-source proxy server that runs HTTP/HTTPS, SOCKS4/5, SNI and TCP/UDP port-mapping proxies from one config file. Use when a user asks to set up an HTTP or SOCKS5 proxy, add proxy users and passwords, write 3proxy access rules, chain or rotate upstream (parent) proxies, limit bandwidth, connections or monthly traffic per user, run 3proxy in Docker, or fix a 3proxy.cfg that will not start.

installs
0
GitHub stars
180
Updated
Oct 25
TerminalSkills
Community

a2a-protocol

Builds Agent2Agent (A2A) servers and clients, the open protocol (originally from Google, now under the Linux Foundation) that lets AI agents from different frameworks call each other. Use when the user wants to create an A2A-compliant agent, build an Agent Card, implement task management, connect agents across frameworks, set up agent discovery, handle streaming responses, implement push notifications, or orchestrate multi-agent workflows. Trigger words: a2a, agent to agent, agent2agent, a2a pro

installs
0
GitHub stars
180
Updated
Oct 25