关于 Equation.io
Equation.io 是内置计算机代数系统的在线绘图计算器,用于探索数学方程。它可渲染 2D 和 3D 图形,并将图形状态保存在可分享的 URL 中。
主要功能
- 01绘制 2D 曲线和 3D 曲面
- 02基于 GPU 的 WebGL 渲染
- 03内置计算机代数系统
- 04绘制向量场和 ODE
- 05可分享的方程 URL
- 06在 MCP 中渲染图形
适用场景
- 01绘制并分享方程
- 02探索向量场和微分方程
- 03可视化概率分布
- 04在 MCP Apps 中展示交互图形
MCP 相关
Equation.io 提供无状态的 Streamable HTTP MCP 服务,支持校验并编码图形 URL、解码 URL 以供编辑,以及在 MCP Apps 宿主中显示交互式绘图器。
配置
远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。
https://equation.io/mcpclaude mcp add --transport http io-equation-equation https://equation.io/mcp{"mcpServers":{"io-equation-equation":{"type":"http","url":"https://equation.io/mcp"}}}在 Cursor 中打开{"servers":{"io-equation-equation":{"type":"http","url":"https://equation.io/mcp"}}}在 VS Code 中打开{"mcpServers":{"io-equation-equation":{"type":"http","url":"https://equation.io/mcp"}}}{"mcpServers":{"io-equation-equation":{"serverUrl":"https://equation.io/mcp"}}}命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。
工具
3| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| equations | array | 是 | 图表中的每个方程,按显示顺序排列。每个字符串对应一个方程或定义——不要用 ";" 合并行(引号内的 ";" 属于数据,会保留)。 |
详细介绍
Equation.io
equation.io — 一款内置 CAS 的绘图计算器。输入方程,它们会编译成 GPU 着色器,并渲染为 2D 曲线、3D 曲面、向量场、ODE 相图、概率密度等。每张图都完全保存在其 URL 中,因此地址栏就是分享按钮。
graph.tk 的故事
这是 graph.tk 的继任项目。graph.tk 于 2010 年 5 月在此仓库中启动,最初是一款基于 HTML5 画布的绘图工具,多年来获得了 400 多颗星。网站运行在免费的 .tk 域名上——后来证明这是致命缺陷:注册商(Freenom)最终接管了该域名并在上面投放广告;Meta 起诉 Freenom 后,整个 .tk 注册局也随之崩溃,该域名彻底无法解析。
我们吸取了教训,从头重建了绘图工具——采用全新的解析器和 CAS,并使用 WebGL 替代画布进行渲染——运行在一个真正归我们所有的域名上:equation.io。原始代码保存在 legacy 分支(标签 graph.tk-final)中,遵循原有的 LGPL-3.0 条款;main 分支中的所有内容都是以 MIT 许可发布的全新重写版本。旧版界面仍可在 graph.equation.io 使用。
架构
部署为 Cloudflare Worker。页面位于 web/;数学功能位于 lib/。
lib/expr.ts包含分词器、调度场解析器和表达式树。lib/defs.ts负责解析名称和定义。解析时的展开逻辑与其实现的数学功能放在一起:vector-calculus.ts(梯度、散度、旋度)、sums.ts(Σ 和 Π),以及integrate.ts、curves.ts和surface-geometry.ts中的积分、曲线和曲面处理流程。- 分布相关代码位于
lib/dist.ts。它重新导出基本分布(dist-base.ts)、连续采样(dist-sample.ts)、离散分布(dist-discrete.ts)和随机变量系统(dist-system.ts)。分布族表位于dist-families.ts。 - 几何、张量、测度、列表和自动机组成了
lib/的其余部分:geom.ts、pga.ts、clifford.ts、tensor.ts、measure.ts、list.ts、automaton.ts及其旁边的模块。compiler.ts将分类后的行转换为 CPU 计划和 GLSL 计划(glsl.ts)。 web/main.ts是方程编辑器和启动入口。web/session.ts管理文档、面板和共享的 WebGL 渲染器。web/scene.ts构建单帧并将其交给 2D 和 3D 渲染器。这三者共享的可变状态位于web/app-state.ts。- 每个方程都会编译为 GLSL 标量场 F,其零点集就是图像:
- 2D:全屏四边形片元着色器;当距离估计值 |F|/|∇F| 小于一个像素时绘制曲线,并通过双尺度一致性测试排除极点/渐近线处的假线(例如
y=tan(x))。 - 3D(出现
z时自动启用):射线步进隐式曲面——沿每条射线检测符号变化、通过二分法细化、使用有限差分计算法线,并设置gl_FragDepth以确保多个曲面能正确相交。不含z的方程会沿 R³ 中的真实轨迹延展。
- 2D:全屏四边形片元着色器;当距离估计值 |F|/|∇F| 小于一个像素时绘制曲线,并通过双尺度一致性测试排除极点/渐近线处的假线(例如
整个图像状态都保存在 URL 中(/g/eq1;eq2;…,每个方程通过 lib/link.ts 进行百分号编码;该模块还会转义括号,避免聊天应用的链接识别器截断 URL;旧版 /#… 链接仍可加载),因此任意方程组都可以通过链接分享,地址栏就是分享方式。
面向 Agent 的接口:
/llms.txt— 链接格式和表达式语法参考 (web/public/llms.txt)/g/<eqs>— 图像链接的分享形式;Worker 会注入 og:/twitter: 元标签(将图像中的方程作为标题,显示在网站卡片上)/mcp— 无状态 MCP 服务(Streamable HTTP),提供encode_graph_url(验证行并返回链接)、decode_graph_url(解码链接以便编辑)和show_graph(在 MCP Apps 宿主中渲染交互式绘图器)。 请参阅 MCP Apps 集成与测试。/.well-known/ard.json(也可通过旧版/.well-known/ai-catalog.json访问) — Agentic Resource Discovery 目录,列出 MCP 服务;/mcp/server-card— 其 MCP Server Card。两者均由server.json生成,生成逻辑位于worker/discovery.ts。
使用方法
pnpm web # dev server (grapher + worker API)
pnpm test # vitest
pnpm typecheck # lib + web + worker
pnpm web:build # build to dist-web/ (client + worker)
pnpm deploy # build and deploy to Cloudflare
流量和 MCP 使用情况
pnpm traffic 7 # needs `wrangler login`
网站访客数据来自 Cloudflare 自带的区域分析功能。脚本每天按页面统计独立 IP 数,仅使用浏览器 User-Agent,并按客户端列出 /mcp POST 请求。网站上没有任何内容会收集这些数据。
MCP 工具调用是 Cloudflare 无法看到的唯一内容,因为它不会记录请求正文。worker/mcp-usage.ts 会为每次 tools/call 写入一个 Analytics Engine 数据点:包括工具、客户端、图像类型、行数和错误数,但绝不记录方程文本。设置具有 Account Analytics: Read 权限的 CLOUDFLARE_API_TOKEN,脚本才会纳入这些数据。
示例
基础用法
y = x^2·x^2+y^2=4·y = tan(x)— 2D 曲线y = sin(2πx)·θ = 1; r = θ x·y = x³— Unicode 输入:π、 希腊字母名称、上标指数、下标(T₀≡T_0,因此a₃是数列项),以及·/×/÷/≤/≥/≠; 在编辑器中,输入\pi、\theta、\nabla、……会插入相应符号,而在任意函数名称前输入\会直接将其去掉(\trail→trail)z = sin(x)cos(y)·x^2+y^2+z^2=9— 3D 曲面(出现z时自动绘制)y < x/2 + 1— 不等式会为其区域着色;严格不等式</>不显示 边界,<=/>=会绘制边界线,像4 <= x^2 + y^2 <= 9这样的链式不等式会按每个非严格界限绘制边缘y = {x < 0: -x, x >= 0: x^2}— 分段函数:按顺序尝试cond: value条件,最后可选一个不带条件的值作为默认值;条件可以串联,例如{0 < x < 1: 1, 0},不带条件的值按 1 计({x > 0, 5})y = {0 < x < 2: x^2}— 定义域限制:没有默认值时,条件之外的值 未定义,因此不会在该处绘制sin(x)cos(y)— 只含 x、y 的裸表达式是 2D 标量场;正值区域以行颜色着色, 负值区域则以其补色着色。sin(x)也属于标量场(沿 y 方向恒定):曲线为y = sin(x)2+2、sqrt(a)、|A - B|— 裸数值不会绘制图形,而会在行下方显示= 4,并随滑块和t实时更新;要绘制直线,请写y = 4
滑块和动画
a = 2— 带滑块的命名常量;其他方程可以使用a,它会编译为 uniform,因此拖动滑块时无需重新构建着色器。b = a^2 + t定义一个计算得出的/动画常量a := 2、f(x) := x^2— 显式写出的相同定义。:=也可以将e、pi、i、w或t作为值(而非函数)接管,并应用于整个图形(e := 0.6是滑块;t := 0.5会停止所有动画);u和v接受一个区间(u := interval(0, 2pi));x、y、z和d仍是语言内置变量(2, 3)/(3, 12, 0)— 点。在 2D 中,可以在画布上拖动由普通数值 或滑块名称组成的坐标,拖动时会重写这些坐标:a = 1; b = 2; (a, b)会移动两个滑块,(2sin(t), 3)只会移动其字面量高度(2cos(t), 2sin(t))—t表示自加载以来经过的秒数,因此此点会绕圈运动
微积分
f(x) = x^3 - a x— 用户定义函数,会以符号形式内联展开f(z) = {re(z) >= 1: 1, f(4 - 3(z^6)^(1/6))}然后f(x i - |y|) >= 0— 尾递归函数(每次自调用都必须是其{…}中的完整分支) 会针对每个像素作为有界循环运行;此例会为科赫雪花着色y = d/dx f(x)/d^2/dx^2 (x^4)— 符号化莱布尼茨导数;适用于 任意单字母变量,可以嵌套,也能在函数定义中传递:g(x) = d/dx f(x)然后y = f(a) + g(a)(x - a)会生成实时切线
概率
X ~ Normal(0, a)— 随机变量;该行会绘制其概率密度,参数可以使用滑块。接着,P(X < b)、P(X > b)或P(-1 < X < 2)会为密度曲线下的对应区域着色,并显示数值概率- 还支持
Uniform(lo, hi)、Exponential(rate)、Gamma(shape, rate)、Beta(a, b)、ChiSquared(df)、StudentT(df)(或T(5))、LogNormal(mu, sigma)、Cauchy(location, scale)、Weibull(shape, scale)— 精确概率密度、精确的P(…),以及在重尾分布无法报告 σ 时显示的中位数/四分位距读数 erf、normalpdf(x, mean, sd)和normalcdf(x, mean, sd)也都是普通 函数,因此y = normalcdf(x, 0, 1)会绘制累积分布函数
向量场和常微分方程
(-y, x)——一个依赖于 x、y 的元组,是一个向量场,通过 GPU 线积分卷积渲染为动态流线;t也可以:(cos(t)-y, x)grad(x^2 + y^2)(或∇(…))——符号梯度,以元组表示,因此会绘制为向量场,也可用于dot(grad(f), (1, 0))dy/dx = x y/y' = sin(x) - y——ODE 会绘制斜率场/方向场(1, f);点击画布可在该点生成一条 RK4 积分曲线,双击可清除(x', y') = (y, -sin(x))——系统会绘制相图,同样可通过点击追踪轨迹
模拟(状态)
th' = om(角度),om' = -sin(th)(角速度),且th(0) = 3——一个状态:自定义名称后的撇号表示该变量对 t 的导数;图表动画播放时,变量会以固定步长通过 RK4 向前积分——参见lib/state.ts。在其他所有场景中,th都与常量完全一样,具有均匀性等特性,因此绘制系统就是普通绘图:(sin(th), -cos(th))表示摆球,(u sin(th), -u cos(th))表示摆杆。它是图表中唯一一个不是关于t的公式的值,这正是双摆(混沌且无闭式解)得以实现的关键。初始值会显示一个滑块,可重新启动运行;面板中的 ↻ 可重启运行r' = vel,vel' = -r/|r|^3,且r(0) = (1, 0)——一个向量状态:导数为 2 或 3 维向量时,会按分量分别对r_1、r_2(及r_3)积分;裸名称会绘制为一个运动点,并参与点运算——在两行中表示一条轨道label((2, 4), "peak")/label(A, "vertex")——在点旁显示文本,颜色与所在行相同;点会像其他点一样随滑块和t移动y = x^2 #e24——以十六进制颜色开头的注释会使该行按此颜色绘制trail(A)——在 2D 或 3D 点后留下动态运动轨迹。 例如,A = (cos(t), sin(t)); trail(A)会在运行时绘制一条轨道;trail((cos(t), sin(t), t/5))会绘制一条上升螺旋线。向量状态也适用。 轨迹最多保留 30 秒/2048 个观测位置;方程或模拟重启时会重置,并且仅限当前会话使用。p(0) = ([0..299]/30, 0, 0)——一个状态族:一组起始值会让系统针对每个元素各运行一次(最多 1024 次),与其耦合的状态也会随之运行。随后,p会绘制一团运动点,mean(p_1)会计算各次运行的均值。若从元组开始,p(0) = (sort([0..299])/30, 0, 0),各次运行会按顺序排列,p[1]表示第一次运行p(50..400)——一条轨道:表示状态在这些时刻之间的变化,会使用实时模拟自身的步长提前积分(lib/orbit.ts),使运动点沿轨道移动。状态族会为每次运行绘制一条路径(从元组开始运行时,p[1](50..400)会绘制一条路径);标量状态会绘制其随时间的变化曲线,th(0..20)即曲线 (t, th)。两者结合时,Rössler 吸引子会呈现为一条细带状轨道,粒子沿其流动
自定义坐标与复数根
r = sqrt(x^2+y^2); theta = atan2(y,x)定义极坐标。(r, theta) = (2, 9pi/4)会绘制对应的点,角度会按 2π 取模。 使用右侧的字面值或滑块值,可在这些坐标中拖动该点。(r, theta) = (3u, 6pi u)描绘一个三圈螺旋;(r', theta') = (r(1-r), 1)会绘制极坐标极限环场。1+2i会绘制阿尔冈点;w^3 = 1会绘制三个单位立方根。 系统会在当前视图中进行数值搜索;较小的解分支可能会遗漏。示例菜单中提供坐标示例。
矩阵
M = ((a, b), (c, d))——由行组成的元组是一个 2×2 或 3×3 矩阵(元组的方括号形式[(a, b), (c, d)]表示两个点);det(M)、trace(M)、矩阵向量乘法M v和solve(M, v)(克莱姆法则)会在降级时进行符号展开,参见lib/mat.ts。因此,(x', y') = A (x, y)可以绘制一个可通过滑块调节矩阵各项的相图,而om' = solve(M, f)则会以推导出的拉格朗日形式 M(θ)ω′ = f 积分双摆
参数曲线和曲面
(2cos(2pi u), 2sin(2pi u), 3u)——参数曲线,u ∈ (0,1)u^2——单独一行 u、v 中的表达式会绘制其取值:u 在 [0, 1] 上均匀分布时 u² 的密度(cos(2pi u)(2+cos(2pi v)), sin(2pi u)(2+cos(2pi v)), sin(2pi v))——参数曲面,u,v ∈ (0,1);逐片元使用 Newton 法求射线与曲面的交点,并采用光泽镜面材质
序列和数据
a_n = 1/n^2——一个序列:在整数 n ≥ 0 处绘制点;行上的 Σ 开关可切换为部分和 S_N(此序列收敛到 π²/6)a_{n+1} = r a_n (1 - a_n)——一个递推关系:绘制映射曲线、对角线 y = x,以及从初始值a_0开始的蛛网图路径(为滑块定义a_0 = 0.2,默认值为 ½)。当右侧的x未定义时,x 会成为参数轴,图像则为轨道/分岔图:a_{n+1} = x a_n (1 - a_n)是逻辑斯蒂分岔[3, 1, 4, 1, 5]——一个数据列表:在数轴上绘制点图,每个值位于 x = value,其重复项沿竖直方向堆叠(1 出现两次:点位于 (1, 1) 和 (1, 2))。[(1, 2), (3, 4)]是散点图
回归
P = [(0, 1), (1, 3), (2, 5), (3, 7)]; P.y ~ m P.x + b会拟合一条穿过这些点的直线。P.x和P.y是这些点坐标组成的列表,按数据文件列的方式逐点配对(分别编写的两个列表X、Y相互独立,因此(X, Y)会形成网格)。未绑定的名称m和b会成为拟合常数;y = m x + b会绘制模型,而(P.x, P.y - (m P.x + b))会绘制残差。拟合行会报告系数、RMSE、R²(适用时)和观测数。P.y ~ a P.x^2 + b P.x + c会拟合多项式;P.y ~ a exp(b P.x)会拟合非线性模型。已定义的常数保持固定,并在更改这些常数时重新拟合其他系数。请在拟合式上方定义数据和固定常数。data.height ~ m data.age + b可用于 CSV 列。缺失或非有限的数据对会被跳过,并附带数量统计;长度不匹配和系数无法识别会报错。共享链接中的缺失 CSV 文件仍保留在本地设备上。- 拟合是静态的,最多支持 8 个系数和 10,000 个观测值(非线性模型为 2,000 个)。非线性拟合使用确定性初始值,并报告局部拟合结果;不保证得到全局最优解。
傅里叶分析
s = interval(0, 1); fourier(cos(4pi s) + 0.5sin(10pi s))——单边振幅谱。区间是信号的变量及其周期,因此频率的单位是该变量的周期数;函数会在一个周期内读取,如fourier(f(s))。默认使用 256 个样本;fourier(f(s), 512)可设置样本数。N = clamp(round(5), 0, 48); y = reconstruct(f(s), N)——带符号的均值加前 N 个谐波,并保留相位。可在 N 后指定可选的样本数。傅里叶分析示例会在关联视图中显示原始信号、频谱和重构结果。S = sort(data.signal, data.row); fourier(S)——按文件顺序排列的等间距 CSV 样本,覆盖 [0, 1);也可使用fourier(S, interval(0, 10))。元组也适用,例如fourier((0, 1, 0, -1));方括号列表没有顺序。样本必须是有限实数,且不能有间隙。两种操作都假定信号是周期延拓的静态信号;信号滑块变化时会重新计算系数。重构最多支持 128 个谐波;样本数范围为 2 到 4096。
上下文语法帮助 方程编辑器会在输入时建议函数、定义名称和已加载的 CSV 列,并在函数调用中显示签名。按 Tab 或单击可插入建议;使用方向键选择建议,再按 Enter 插入。其他情况下,按 Enter 会新建一行方程,按 Escape 会关闭帮助,补全会作为一次可撤销的文本编辑处理。注释和带引号的字符串不会触发建议。
数论与复分析
gcd(a, b)/isprime(n)— 数论;试试a_n = isprime(n)ln(w-2) - ln(w+2)— 复分析:i是虚数单位,且w = x + iy;复值表达式会绘制其虚部的等值线(电场线)和实部的等值线(等势线),因此可以直接用复势绘制静电场。re/im/arg/abs/conj可将值转换回 ℝ,例如im(ln(w)) = 1会绘制成普通的隐式曲线
方程会保存在 URL 哈希中。拖动可平移/旋转,滚轮可缩放,在 3D 中右键拖动(或按住 Shift)可平移;单击颜色圆点可切换颜色。光标悬停在点和放置的 ODE 种子上时,它们会高亮显示,并可随光标拖动。方程面板是固定在角落的卡片:轻甩它——触摸卡片任意位置,或用鼠标沿卡片顶部边缘拖动握柄条——即可将它移到任意角落;也可以将它甩过任意边缘,彻底清空视图;面板会跟随指针,并沿甩动方向离开。留下的 y= 标签可用于恢复面板(轻触它,或拖动它将面板拉入),选定的角落会保持不变。
worker/ — Cloudflare Worker 入口:提供构建后的应用,并处理 /api/* 路由。
贡献
你需要 Node 24 和 pnpm。运行 pnpm install,然后运行 pnpm web 启动开发服务器。
提交 PR 前,请运行 CI 执行的相同检查:
pnpm typecheck # tsc over lib, web and worker
pnpm lint # Oxlint, including type-aware rules
pnpm fmt:check # Oxfmt (pnpm fmt rewrites files in place)
pnpm vitest run # unit tests
pnpm lint:fix 会应用 Oxlint 能自动修复的问题。在 VS Code 中,安装推荐的 Oxc 扩展,即可在输入时查看 lint 错误。
许可证
MIT — 参见 LICENSE。legacy 分支上 2026 年之前的 graph.tk 代码仍遵循其原有的 LGPL-3.0 条款;当前代码库未复用其中的任何代码。
坐标轴缩放
按住 Option/Alt 并拖动 2D 画布,可分别缩放各坐标轴:水平移动缩放 x 轴,垂直移动缩放 y 轴,缩放锚定在初始指针位置。普通缩放会保持比例。在视口行中设置 ratio = 1,即可恢复坐标轴单位等长。
缩放会创建或更新可分享的视口行:
view(x = -10..10, y = -1..1, ratio = 5)。正值 ratio 表示每个 y 单位对应的像素数除以每个 x 单位对应的像素数;省略时默认为 1。系统会在保持该比例的同时调整边界范围,包括在不同屏幕尺寸下。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。