OpenSCAD Rounded Edges: Minkowski vs a 2D Rounded Profile, Measured

2026-11-10

The question

There are two common ways to round the edges of a box in OpenSCAD:

  1. 2D rounded profile. Draw a rounded rectangle with offset(r = …) and linear_extrude it.
  2. Minkowski. Take a smaller cube and minkowski() it with a sphere().

Forum answers usually say Minkowski is "slow" and the profile is "fast". We wanted numbers: how much slower, for which shapes, and at which $fn? So we built controlled models and rendered each one seven times in this site's OpenSCAD playground.

One thing to say up front: the two methods don't make the same shape. The profile rounds only the four vertical edges. Top and bottom edges stay sharp. Minkowski rounds all twelve edges and all eight corners. So this is a comparison of cost for two different designs, not two ways of computing one result. Pick the shape you need first, then use the numbers to judge the cost.

The models

Every model has the same outer size: 40 × 30 × 20 mm with a 3 mm rounding radius. We rendered each at $fn = 16, 32 and 64.

// 2D rounded profile: rounds the 4 vertical edges only
length = 40; width = 30; height = 20; radius = 3;
$fn = 32;
linear_extrude(height = height)
  offset(r = radius)
    square([length - 2 * radius, width - 2 * radius], center = true);
// Minkowski: rounds all 12 edges and 8 corners
length = 40; width = 30; height = 20; radius = 3;
$fn = 32;
translate([0, 0, height / 2])
  minkowski() {
    cube([length - 2 * radius, width - 2 * radius, height - 2 * radius], center = true);
    sphere(r = radius);
  }

We added three more:

  • Hull of 8 spheres. The same rounded envelope as Minkowski, built with hull(). This is the usual "faster" alternative.
  • Trays. A tray with a rectangular pocket, once as a rounded profile with the pocket cut out, and once as a Minkowski of the pocketed core. The pocket makes the core non-convex, which turns out to matter a lot.
  • Plain cube. A 40 × 30 × 20 cube(), to show the fixed cost of a render with nearly no geometry.

Isometric view: the rounded-profile box with sharp top edges next to the Minkowski box rounded on every edge

Front view: the profile box is a sharp rectangle, the Minkowski box has rounded top and bottom edges

How we measured

  • Engine: the production playground on 10 November 2026: OpenSCAD 2025.01.19 with the Manifold 3.0.1 backend, compiled to WebAssembly and running in a Web Worker.
  • Browser and machine: Chromium 145 (headless, Playwright) on Linux with an AMD Ryzen 7 5825U (8 cores, 16 threads).
  • Runs: 7 measured renders per model. Each run opened a fresh page, waited for the automatic preview to finish, then clicked Render and timed it. Models were interleaved, so any background load affected all of them alike. Before each run the script waited until the whole machine was under 40% CPU. Load average stayed between 0.7 and 2.4.
  • What we report: compile is the time OpenSCAD spent evaluating and meshing the model, from the worker's "compiling" phase to "complete". Total is from clicking Render to receiving the mesh, which also includes starting the engine for that render (about 270–430 ms here).
  • Checks: for every run we parsed the STL and recorded the triangle count, bounding box and non-manifold edges. All successful renders were watertight (0 non-manifold edges).

We ran the benchmark twice before this on the same machine while other work was running (load average up to 41). Those numbers were noisy, so we don't use them. The pattern was the same.

Results

Medians of 7 runs. The ranges in brackets are min–max.

Model$fnTrianglesBounding box (mm)Compile, msTotal, ms
Plain cube—1240 × 30 × 2049 [46–52]445 [315–491]
Rounded profile167640 × 30 × 2053 [52–60]333 [324–525]
Rounded profile3214040 × 30 × 2053 [27–56]332 [328–495]
Rounded profile6426840 × 30 × 2054 [28–55]343 [326–483]
Minkowski1631639.885 × 29.885 × 19.88581 [64–91]489 [344–552]
Minkowski321,14839.971 × 29.971 × 19.971126 [102–144]418 [396–592]
Minkowski644,34839.993 × 29.993 × 19.993253 [250–254]526 [521–727]
Hull of 8 spheres1631839.885 × 29.885 × 19.88581 [75–90]497 [352–547]
Hull of 8 spheres321,16239.971 × 29.971 × 19.971158 [142–176]565 [425–617]
Hull of 8 spheres64——failedfailed
Tray, profile169240 × 30 × 2058 [53–71]351 [326–532]
Tray, profile3215640 × 30 × 2055 [52–61]344 [330–522]
Tray, profile6428440 × 30 × 2053 [52–56]334 [326–482]
Tray, Minkowski1635639.885 × 29.885 × 19.885251 [247–255]525 [523–772]
Tray, Minkowski321,22039.971 × 29.971 × 19.971442 [441–454]719 [709–987]
Tray, Minkowski644,48639.993 × 29.993 × 19.9931,186 [1,158–1,203]1,466 [1,428–1,696]

What the numbers say

The rounded profile costs about the same as a plain cube. Compile time stayed at about 53 ms at every $fn, within a few milliseconds of the 12-triangle cube. Raising $fn from 16 to 64 only adds points to one 2D outline, so the mesh grows from 76 to 268 triangles.

Convex Minkowski is slower, but not dramatically in this engine. At $fn = 32 it took 126 ms against 53 ms for the profile, and at 64 it took 253 ms. The bigger cost is the mesh: 4,348 triangles at $fn = 64, 16 times the profile. Every later operation on that solid (more booleans, slicing, export) has to handle those triangles.

A non-convex Minkowski is where the cost shows. Putting a pocket in the core made Minkowski 5 times slower at $fn = 16 and 22 times slower at $fn = 64 (1,186 ms against 53 ms). The time roughly doubled each time $fn doubled. The usual reason: a Minkowski sum of two convex shapes can be computed as one convex hull, while a non-convex shape has to be split into convex parts first, and each part is combined with the sphere separately.

Isometric view of the two trays: the profile tray has sharp top edges and a sharp pocket rim; the Minkowski tray is rounded everywhere, including the pocket rim

The Minkowski tray also isn't the same object as the profile tray: its pocket rim and pocket corners are rounded. Minkowski grows the core in every direction, so it shrinks every pocket by twice the radius. We cut a 26 × 16 mm pocket into the core so that the result has the same 20 × 10 mm opening and 9 mm floor height as the profile tray. If you switch methods on a real part, resize the pockets the same way.

Hull didn't help here, and failed at $fn = 64. The hull of eight spheres made almost the same mesh as Minkowski and was a little slower at $fn = 32 (158 ms against 126 ms). At $fn = 64 it failed every time (3 of 3 runs on a quiet machine, plus 11 of 11 in the earlier runs) with this error:

ERROR: CGAL error in applyHull(): CGAL ERROR: assertion violation!

Desktop OpenSCAD may behave differently. In this browser build, prefer Minkowski for the rounded envelope, or keep $fn at 32 or below for hull.

Minkowski boxes come out slightly small. The bounding box is 39.885 mm instead of 40 at $fn = 16 and 39.971 at $fn = 32. OpenSCAD's sphere() is a faceted mesh whose widest points fall slightly inside the true sphere (it has no vertex at the poles, for example), so the box is short by up to 0.115 mm on each axis. The profile has vertices on the axes and comes out exactly 40 × 30 × 20. For a part that has to fit, this can matter more than the render time.

Which one to use

  • Only the vertical edges need rounding (a case, a bracket, a tray that sits on a bed): use the profile. It is the cheapest option and the dimensions are exact. The sharp bottom edge is also the easier one to print.
  • Every edge needs rounding, on a convex block: Minkowski is fine in this engine. Keep $fn at 32 unless you need a smoother surface, and remember the faceting undersize.
  • Every edge needs rounding, on a shape with pockets or holes: expect Minkowski to be slow, and slower at higher $fn. Round the convex outer block first and subtract the pockets afterwards, or render it at a low $fn while you design and raise it only for export.

Limitations

  • One machine, one browser and one OpenSCAD build. Desktop OpenSCAD, the CGAL backend, other CPUs and other browsers will give different absolute times. The ratios are the useful part.
  • Small, simple models. A real part with many features can hit very different costs.
  • Seven runs per model is enough to see the medians clearly, not to measure small differences reliably. The min–max ranges show the spread.
  • We timed renders, not previews. The playground's automatic preview took part of each page load and isn't in the table.

Try it yourself

Each link opens the model in the playground. Press Render to time it on your machine.

Change $fn to 64 in the Minkowski tray to see the cost grow.

The Rounded Box (minkowski) lesson lets you switch between the two methods with one variable.

Related guides: