KCL part 0
For roughly the past year, I've been working with the good folk at Zoo on their CAD product, the Zoo Modelling Studio (aka ZMS, aka KittyCAD). I've mostly been working on the design and implementation of KCL, a domain-specific language for CAD. Since that work has just wrapped up, and it was super-interesting work that I thought others might be interested in, I plan to write a few blog posts about KCL and some of the its features that I worked on. In this first post, I'm going to mostly give some context about KCL.
KCL is a programming language for describing CAD documents. It is part of ZMS, so to understand the constraints on KCL, you need to understand a little about ZMS. ZMS is a new (1.0 was released in April 2025) CAD tool. It is unique for several reasons: it has a client-server model (the client is an Electron app with a TypeScript frontend and Rust/WASM core, the server runs in the cloud and does the core CAD stuff including rendering), it is code-first (the KCL code fully describes the content and is the source of truth), and it can do AI/ML stuff (which is facilitated by being code-first). The client app (including all the KCL stuff) is open source: GitHub repo.
It's worth digging into the 'code-first' aspect since this gives rise to a lot of the uniqueness of KCL. ZMS has a graphical UI which allows all the standard CAD editing operations. These operations edit the KCL source code and the code is interpreted which results in calls to the server API. The server computes the scene and sends video back to the client. Some things (mostly viewing-related, such as the camera position and orientation) are not part of the code and are sent to the server independently. The code can also be edited directly and ZMS functions like an IDE in that mode.
The above context shapes the 'architecture' of KCL. It is an interpreted language (the interpreter runs client-side and is implemented in Rust compiled to WASM). It can be edited in a regular text editor (and there is a CLI to run it from the command line outside of ZMS, though that is not a primary use case) or via the IDE built into ZMS. It can also be edited indirectly by using the ZMS GUI.
KCL is very domain-specific; it is not a general purpose programming language. The primary audience is mechanical engineers and other CAD users, not software engineers (more on this below because it is important). KCL is describing a static scene (there is no interactivity or IO, though editing can be interactive), so KCL is technically just data description. However, the domain really benefits from reuse, abstraction, iteration, etc. For example, we often want to use the same component (like a screw) multiple times in different places or to parametrise components (e.g., to make different sizes of screw).
The performance characteristics of KCL are interesting. We are not using it for anything real-time or interactive, nor do scenes get large enough for performance to be straightforwardly an issue. However, because editing is interactive, performance is important (for example, the user might want to drag a control point and the scene should smoothly transform). Because of the distributed nature of the application, there is latency between the interpreter and the CAD server, so the kind of performance considerations common in distributed systems are relevant (e.g., batching, caching, distributing work).
The overall ethos of the language is to be high-level, easy to understand for people without much programming background, have a gentle learning curve, and to do one job (CAD) well (as opposed to being generally useful). Although much of the design philosophy is similar to other programming languages (around things like composability, ergonomics, and expressiveness), the over-arching goal is to be effective as part of a CAD tool for an audience of CAD users.
From a programming languages perspective, KCL is dynamically typed (though not a very dynamic language, e.g., there is no eval
, and little polymorphism). It is 'functional', in the sense that first-class and higher-order functions exist, though they have limited utility. KCL is mostly monotonic, though is not strictly referentially transparent. It is fully deterministic. It is more declarative than imperative, though I don't think it has the overall feel of a functional programming language. Evaluation is strict, not lazy.
So, back to the audience. I think the biggest thing which makes language design for KCL unique is that most users are mechanical engineers, not software engineers. This is both very different to general purpose programming languages, and also much less defined - the level and kinds of programming experience among mechanical engineers is likely much more varied than among software engineers. Some have extensive experience, maybe due to hobbies and interests, or a previous career, or because of some specialisation. Many have experience with tools like Matlab or have done a decent amount of programming at university or for some kind of scripting use case (Python seems to be the most likely language here). But many have done no programming at all. They are likely to have a strong engineering mindset, and experience with software and tools which are programming adjacent. There is a temptation to treat these users like beginner software engineers (since as a software engineer myself, I have been a beginner software engineer, but I have not been a mechanical engineer), or worse as just 'bad' programmers. However, that would be a wildly incorrect model. Our audience have deep experience (and the ingrained patterns of working which go with that), different perspectives, and different expectations.
As I mentioned earlier, there is also the AI/ML use case. Since KCL is a new language there is not much code to train AI on. So models have to already 'be able to program'. That means they often expect to write KCL which is more like existing languages. So even though the human users might not have experience with many languages, the AI 'users' do. The AI seems to expect some things based on naming (and whatever else) which I wouldn't expect from most users. I don't think I have much insight into language design for AI, so I probably won't touch on this in these blog posts, but I think it is an interesting point which came up in practice.
For more details about KCL, see the book or the language reference.
An example
Here's a very simple example, from https://zoo.dev/docs/kcl-samples/pipe, a simple pipe:
// Define parameters
pipeInnerDiameter = 2.0
pipeOuterDiameter = 2.375
pipeLength = 6
// Create the pipe base
pipeBase = startSketchOn(XZ)
|> circle(center = [0, 0], radius = pipeOuterDiameter / 2)
|> extrude(length = pipeLength)
// Extrude a hole through the length of the pipe
pipe = startSketchOn(pipeBase, face = END)
|> circle(center = [0, 0], radius = pipeInnerDiameter / 2)
|> extrude(length = -pipeLength)
|> appearance(color = "#a24ed0")
Some things to note:
- All the basics you'd expect from a programming language are there: numbers (there's only one kind of number in KCL, no distinction between floats and integers or different precision), strings (used for a colour in the example), variables (which are immutable and don't require a keyword to declare), arithmetic (there's logical operators too), arrays (used in the example for 2D points), and function calls (note that 'all' arguments are named).
- The most obvious innovation is the pipeline syntax -
... |> ...
this is pretty handy for creating objects; the result of a function is passed as input to the next function in the pipeline (which is why 'all' is in quotes above for describing named arguments, the input argument is not named; it can also be used outside of a pipeline, but more on that in another post). - There's a pretty extensive standard library which covers basic programming stuff like finding the length of an array, but also a lot of CAD-specific stuff (e.g.,
circle
andextrude
, and constants like the planeXZ
).
There's lots of sample code online which you can browse to see the code and the 3D objects it produces.
My contributions
KCL had been around for a while before I started working on it and the general feel and character of the language and its implementation were well-established. I spent almost a year working on both the design and implementation, adding some features and refining others, and making the implementation more performant and (hopefully) easier to work with and extend. I'll cover some of the more interesting things in following blog posts, but here's a summary of some of the smaller or less blog-able things (of course none of this I did by myself, all of the below and the things I'll blog about were done in collaboration with the rest of the team at Zoo, in particular Adam Chalmers and Jon Tran who worked (and continue to work) on KCL):
- modules/assemblies - this was a key feature for the 1.0 launch. Assemblies are how components are assembled into complete CAD designs and in KCL the module system follows from the concepts of components and assemblies. I designed and implemented a system which was ergonomic for programming as well as fitting into an ergonomic flow for editing via the UI. I also designed an extension for shared libraries of components (which hasn't been implemented yet).
- types - KCL is dynamically typed and mostly uses types for documentation and IDE functionality. I refined and evolved the types and implemented dynamic checking in the interpreter. I implemented a new documentation system which is driven by declared types (an improvement on the previous system which relied on the Rust implementation of the language and standard library).
- Engineering and usability improvements. I worked on numerous smaller issues which together made KCL a better user experience and easier for implementers. I improved errors and warnings, added a system for experimental features, improved performance, refactored the implementation (in particular to be more type-driven, rather than relying on implementation details), started an API design to more cleanly separate the frontend from the interpreter, improved the interface between Rust and KCL (which allowed completely describing the standard library functions using KCL signatures and implementing some standard library functions using KCL), plus fixed bugs, and polished the user experience with numerous minor improvements to IDE features, error messages, documentation, and so forth.
It's been hugely fun to work on KCL, in part because it is so different from Rust. It's been a great challenge to think of the audience and the specifics of the domain, as well as to consider the very different performance requirements. I think the product and language have great potential and I hope to see them both take off.