# kurbo: a Rust 2D curve library built around accuracy

> Kurbo is a Rust library of data structures and algorithms for curves and vector paths, developed by the Linebender project, and its distinguishing choice is accuracy over convenience: approximate functions take an accuracy parameter, analytic solutions are used where they are practical, and area is computed with Green's theorem. It is also a library that says plainly that it is still in early development.

**linebender/kurbo** — A Rust library for manipulating curves

- Repository: https://github.com/linebender/kurbo
- Stars: 1,022 · Forks: 107
- Language: Rust
- License: Apache-2.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/linebender-kurbo

## Curves and paths, with accuracy as the selling point

Kurbo contains data structures and algorithms for curves and vector paths, and the README is candid about who it is for. It is probably most appropriate for creative tools, but is general enough it might be useful for other applications. The name is Esperanto for curve.

The differentiator is stated in the next paragraph. There is a focus on accuracy and good performance in high-accuracy conditions, and the library might be useful in engineering and science contexts as well, as opposed to visual arts where rough approximations are often sufficient.

That sentence defines the project against the rest of the ecosystem. Plenty of curve libraries exist for graphics, where a tolerance of a hundredth of a pixel is invisible and a fast approximate solution is preferable. Kurbo is for the case where the answer has to be right to a number you chose, and where an approximation that is fast but off by a fraction of a unit is a bug rather than a rounding artefact.

The implementation approach follows from that. Many approximate functions come with an accuracy parameter, and analytical solutions are used where they are practical. The example given is area calculation, which is done using Green's theorem, that is, by integrating around the boundary of the shape rather than by sampling the interior.

So the library is two things at once. A Bézier and path toolkit for building and editing vector geometry, and a numerical library that will spend real effort to give you an answer within the tolerance you asked for.

## f64 coordinates and an accuracy parameter on the approximate calls

Two design choices in the README explain most of how the library feels in use.

The first is the coordinate type. The similar crates section notes that flo_curves is generic on the coordinate type, while we use f64 for everything. That is a deliberate narrowing. A generic coordinate type lets you use the library with integers, with fixed point types, or with a different float, but it also means every function is generic over it, and the numeric behaviour is then a property of the type you chose rather than of the library. Fixing f64 removes that question, at the cost of not being able to use the library directly with exact integer geometry.

The second is the accuracy parameter on the approximate functions, which is the mechanism the README points at when it explains the engineering focus. A caller that needs a tolerance declares it, and the library spends what it takes to hit it. The practical consequence for a user is that the cost of an operation is not fixed. A call with a loose tolerance is cheap, and a call with a tight tolerance is expensive, and the API makes that trade explicit rather than hiding it behind a default that is either too slow or not accurate enough.

This is why the library mentions engineering and science contexts. A tool that computes the area under a curve, or the length of a path, or the intersection of two shapes, has a tolerance it must respect, and the README's choice of Green's theorem for area is an example of the preference for an exact method over a sampled one.

The traits that would make the library work for general curves, not just Béziers, are the ones to watch. The README warns that they will probably be reorganised, so the shape of the general curve API is not yet settled.

## Green's theorem for area, and analytical solutions where they are practical

The area calculation is the concrete example of the philosophy, and it is worth spending a paragraph on because it shows what the library considers a good answer.

Area calculation is done using Green's theorem. The theorem relates the area enclosed by a closed curve to a line integral around its boundary, which means the area of a path can be computed from the path itself rather than from a mesh of samples or an approximation of the enclosed region. For a Bézier path, the integral has a closed form, so the area is exact up to floating point rounding rather than a numerical estimate.

That is a different result from what a sampling approach gives you. A sampling approach has an error that depends on the sample density, and the error is worst exactly where the shape is hardest, near a cusp or a tight loop. An analytic result has a much smaller and more uniform error.

The README's phrasing, analytical solutions are used where they are practical, is the important qualifier. Not every operation has a closed form, and for the ones that do not, the library falls back on the approximate functions with their accuracy parameter. So the model is: use the exact method where one exists, and where one does not, let the caller state the tolerance rather than baking in a default.

For a reader who is not a computational geometer, the practical advice is this. If you are building a creative tool where the user will not notice a hundredth of a pixel, the difference between the libraries is mostly ergonomics and render focus. If you are building something where the geometry is the answer, a tool that lets you say how accurate you need and then actually delivers it is a different kind of dependency, and that is the case Kurbo is built for.

## The MSRV can move in a patch release, and there is a documented way out

The minimum supported Rust version section has an unusual warning in it, and it is the kind of detail that saves an afternoon.

This version of Kurbo has been verified to compile with Rust 1.85 and later. Future versions of Kurbo might increase the Rust version requirement. It will not be treated as a breaking change and as such can even happen with small patch releases.

So a patch release can raise the compiler floor. That is unusual and worth planning for, because a project with a pinned toolchain can find that a routine dependency bump no longer compiles.

The README anticipates that and gives a way out. As time has passed, some of Kurbo's dependencies could have released versions with a higher Rust requirement, and if you encounter a compilation issue due to a dependency and do not want to upgrade your Rust toolchain, you could downgrade the dependency:

```sh
# Use the problematic dependency's name and version
cargo update -p package_name --precise 0.1.1
```

The version numbers in that snippet are placeholders, obviously, but the mechanism is the real one. Cargo's update command with the precise flag pins a single dependency to a specific version without changing the rest of the lockfile, and because Kurbo's dependencies are the usual suspects for a raised MSRV, this is the first thing to try when a build breaks on an old toolchain.

The consistency requirements behind this are visible in the repository. The Cargo.toml keeps rust-version = 1.85 and says in a comment to keep in sync with RUST_MIN_VER in the CI workflow, with the relevant README files and with the MSRV in the Unreleased section of the changelog. So the number is checked in several places, which is the right way to keep a policy that is enforced across a workspace.

## Three neighbouring crates and the choices that differ

The README has a similar crates section, and it is unusually useful because it explains where the design decisions went.

lyon_geom has a lot of very good vector algorithms and is most focused on rendering. flo_curves has good Bézier primitives and seems tuned for animation, and it is generic on the coordinate type while Kurbo uses f64 for everything. vek has both 2D and 3D Béziers among other things and is tuned for game engines.

Read together, those three sentences describe a whole design space. A library focused on rendering optimises for producing geometry efficiently, and that is a different goal from computing with it. A library tuned for animation optimises for evaluating a curve at a point in time, repeatedly, which is again a different goal from solving for an area or a length. A library tuned for game engines carries 3D as well as 2D.

Kurbo's position in that space is the one where the geometry is the answer rather than an intermediate, which is consistent with the accuracy focus and with the choice of f64 rather than a generic coordinate type.

The README also credits lyon_geom: some code has been copied from lyon_geom with adaptation, and the author of lyon_geom, Nicolas Silva, is credited in the AUTHORS file. The contributors are asked to add their name to that file in any substantive pull request, which is a small convention that makes the provenance of a numeric library legible over time.

For learning, the README points at A Primer on Bézier Curves by Pomax and calls it indispensable, and discussion happens in the Linebender Zulip, in the #kurbo channel, where all public content can be read without logging in.

## The workspace, the lint set and a sibling crate

The repository is a Cargo workspace with two members, kurbo and polycool, and the shared settings live in the root manifest.

```toml
[workspace]
resolver = "2"
members = ["kurbo", "polycool"]

[workspace.package]
version = "0.13.1"

edition = "2024"
rust-version = "1.85"
license = "Apache-2.0 OR MIT"
```

The polycool crate is released on its own cadence, polycool v0.4.0 came out on 2025-10-23 while Kurbo v0.13.0 came out on 2025-11-27 and v0.13.1 on 2026-05-13, so the two members of the workspace version independently. The comment in the manifest explains that the Kurbo version is also used by other packages which want to mimic Kurbo's version, and that bumping it means bumping the others in the workspace.dependencies section too.

The lint configuration is the other thing to look at. The workspace forbids unsafe code outright, with rust.unsafe_code set to forbid, and applies the Linebender canonical lint set, which forbids keyword_idents_2024, non_ascii_idents, non_local_definitions and unsafe_op_in_unsafe_fn, and warns on elided lifetimes in paths, missing debug implementations, missing docs, trivial numeric casts, unnameable types and unreachable pub. A library of numerical algorithms that forbids unsafe code and warns on missing documentation is making a statement about the kind of code it is willing to hold.

The dependency list is small and telling. euclid and libm for the numeric layer, smallvec and arrayvec for the small buffers, rand and arbitrary with arbtest for property based testing, criterion for the benchmarks, and serde and schemars for the serialisation and schema side.

## Where Kurbo is the wrong tool

The limits are explicit in the README and worth reading before you commit.

The first is maturity. The library is still in fairly early development stages, and the general curve traits, the ones intended to be useful for curves that are not just Béziers, will probably be reorganised. That is a statement from the maintainers that the API you would build a general abstraction on is going to move. A project that depends on those traits today should expect to rework them.

The second is the numeric type. Because everything is f64, the library cannot be used directly for exact integer or fixed point geometry, and a project whose coordinates are integers by nature, a pixel grid or a CAD file with integer units, will convert at the boundary and live with the conversion.

The third is scope. Kurbo is 2D. The README itself points at vek when you want 2D and 3D Béziers among other things, and at lyon_geom when you want vector algorithms focused on rendering. A project that needs a 3D pipeline or a rendering focused vector library is looking at a different crate, and the README has already made the argument for the alternatives with a specific difference in each case.

The fourth is the Rust version policy, which cuts both ways. An MSRV that can rise in a patch release is friendly to the library and hostile to a project with a frozen toolchain, and the documented workaround is to pin a dependency rather than to upgrade, which is a decision you may have to repeat.

The fifth is the licence, and it is the easiest of the five. Apache 2.0 or MIT at your option, with contributions dual licensed on the same terms unless the contributor says otherwise, so the terms are the permissive ones most Rust projects use.

## Conclusion

Adopt kurbo if you are writing Rust tooling that computes on curves, such as a CAD kernel, a font or glyph pipeline, a plotter or a geometry heavy creative tool, and if you need results that hold to a tolerance you specify rather than to a rough approximation. Do not adopt it for rendering heavy work where the neighbouring crates are better suited, and do not build on the general curve traits until they settle, because the README says they will probably be reorganised. Verify first that the accuracy parameters and analytic paths cover the operations you need, that your toolchain is on Rust 1.85 or newer, and that the polycool sibling crate in the workspace is not the dependency you actually need.

## FAQ

### What is kurbo used for?

Kurbo contains data structures and algorithms for curves and vector paths. The README says it is probably most appropriate for creative tools but general enough for other applications, with a focus on accuracy and good performance in high-accuracy conditions, which makes it useful in engineering and science contexts as well as visual arts.

### What Rust version does kurbo require?

This version has been verified to compile with Rust 1.85 and later, and the Cargo manifest sets rust-version = 1.85. The README warns that future versions might increase the requirement and that this will not be treated as a breaking change, so it can happen even in a small patch release.

### How does kurbo compute the area of a shape?

Area calculation is done using Green's theorem, so the area is derived from the path rather than from sampled points. The README notes that analytical solutions are used where they are practical, and that the many approximate functions come with an accuracy parameter.

### How do I fix a build failure caused by a dependency needing a newer Rust?

The README documents downgrading the offending dependency instead of upgrading the toolchain, with cargo update -p package_name --precise 0.1.1 as the pattern, using the problematic dependency's name and version.

## Sources

- [Issues](https://github.com/linebender/kurbo/issues)
- [License: Apache-2.0](https://github.com/linebender/kurbo/blob/main/LICENSE)
- [linebender/kurbo on GitHub](https://github.com/linebender/kurbo)
- [README](https://github.com/linebender/kurbo/blob/main/README.md)
- [Releases](https://github.com/linebender/kurbo/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/linebender-kurbo
