February 25th, 2026
This article is a combination of explanation why I started this project and some ramblings on interesting problems I had to solve while working on it.
June of 2024 I got access to an ELEGOO Saturn 3 Ultra resin printer, but was disappointed with the selection of slicers. From what I found, the main choices were Chitubox, VoxelDance Tango, and Lychee, none of which are open source and all had other issues.
For some reason, models sliced with Chitubox were not printing (I was probably just doing something wrong but moving on). I actually really liked VoxelDance Tango but they charge $16/month and limit the number of slicing operations per month, so I didn't make it past the free trial. Online I saw a lot of praise for Lychee Slicer, but when I first ran it to test it out, I swear it took like ten minutes to start, I also had to make an account to use it, there were ads in it, and the slicing was surprisingly slow.
Resin printers build models bottom-up by exposing UV-curing resin with a specific pattern then moving the build plate up for the next layer. I figured that since MSLA slicers really just need to output an image for each layer, it shouldn't be too hard to make my own.
This introduction was taken from my personal website's mslicer project page.
A few years later I'm still working on it, but all things considered it wasn't that hard. When I started I had no idea what I was doing (I was only a Junior in High School :eyes:). I knew I had to somehow intersect a plane with the mesh for each layer, which would give a polygon that could be filled in, but I didn't know the details. Looking back, I think it's a good thing I had to figure everything out from scratch, because I ended up doing some things better than the competition.
After some research, I realized that every triangle in each mesh could be intersected with the layer plane, which, would either yield a line segment or nothing. This process could be accelerated by first partitioning the triangles so only faces close to each layer actually run the full intersection test. Then, to fill the polygons, I decided to use the scan line fill algorithm.1
Because MSLA formats use run-length encoding2 I was able to optimize the polygon filling by outputting runs directly instead of filling a bitmap then compressing it. I can't see the source code of the other slicers, but I suspect this optimization is why mslicer is so much faster than them. Although speed wasn't originally a goal I had for the project, after seeing that I accidentally made something 20 to 120 times faster than competing slicers, I decided to focus more on it. This meant writing custom algorithms for processing layers without decompressing them, which was actually really interesting. That's also why island detection and NanoDLP encoding3 is so fast.
I worked on mslicer for a lot of the summer after I first started the project, but eventually moved onto other things. But I kept coming back every so often and kept making releases every few months. Most of the projects I work on are just for me, so it's been great to have a one that people use and addresses a real need in the space.
Scanline Fill Algorithm
This algorithm works by intersecting a horizontal scanline with your polygon at every y-value. Assuming the polygon is closed, you will get an even number of points, which are the segments of that row that are inside the polygon. Normally you would then fill these segments of pixels with some value.
Run-Length Encoding
Because resin printers often have very high resolution displays/masks it would be impractical to store layer data uncompressed, for this reason all the supported formats make use of some form of run-length encoding (RLE). This is where equal adjacent values are grouped, so
0100001111would be converted to0×1, 1×1, 0×4, 1×4because the sequence starts with one0, then one1then four0s, etc.
NanoDLP Encoding
The NanoDLP format is interesting, but one strange thing about it is that it uses regular PNG images inside a zip file to store the layer data. When I was first implementing the NanoDLP format, I used an existing image processing library but didn't like how the time to encode all the PNGs was slowing down the slicing process more than the intersection tests. So I decided to write a custom PNG encoded optimized specifically for run-length encoded data, since that's white the polygon fill code outputs. I won't go into the details here, but although it took me a while to understand how everything works the actual implementation is not too complicated.