-
3D Printed 20×102mm Cartridge
Having accumulated a box of empty 12 gram CO₂ capsules and having already done Too Many bomb fins:

20x102mm cartridges The capsule is obviously the wrong shape, too short, and only 19 mm diameter, but it’s the thought that counts.
Apply the contour gauge to a genuine slightly battered 20×102mm cartridge:

20x102mm cartridge tracing Scan the sketch, import into Inkscape, rotate the image to correct the case taper angle vs. the page, lay lines & curves around the perimeter, align half of it at the page origin to work with OpenSCAD, export as SVG:

Cartridge – 20x102mm outline – Inkscape layout Import into OpenSCAD, let
rotate_extrudedo the heavy lifting, and remove some pieces:
Cartridge Case – build view solid model The little disk represents a fired primer you’d print separately in a different color and glue into the pocket shown in this cutaway view:

Cartridge Case – cutaway solid model The interior void could hold sand for additional heft, as the whole thing is obviously nose-heavy; that’s certainly in the nature of fine tuning. Obviously, we are not dealing with anything that could go bang.
It builds just like you’d expect:

20x102mm cartridge – printing Dab some adhesive on the capsule tip, ditto for the primer, stick them in place, and it’s all good.
I like the gray PETG-CF version:

20x102mm cartridges – blue gray PETG-CF Maybe not such a good idea in this day & age. Print responsibly, as they say.
Update
Print a sabot to fit a CO₂ capsule into a genuine steel cartridge.
The solid model:

Cartridge Case – sabot solid model The OpenSCAD making it happen:
module Sabot() { tube(SabotOA[LENGTH],id=SabotOA[ID],od=SabotOA[OD],anchor=BOTTOM) position(BOTTOM) tube(SabotOA[LENGTH]/2,id=SabotOA[ID],od=CartridgeOA[ID],anchor=BOTTOM); }The result:

20x102mm cartridges The OpenSCAD source code (minus the sabot) and outline as a GitHub Gist:
LoadingSorry, something went wrong. Reload?Sorry, we cannot display this file.Sorry, this file is invalid so it cannot be displayed.This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters// 20x102mm cartridge // Ed Nisley – KE4ZNU // 2025-05-18 include <BOSL2/std.scad> Layout = "Show"; // [Show,Build] Powder = true; // build internal void /* [Hidden] */ ID = 0; OD = 1; LENGTH = 2; HoleWindage = 0.2; Protrusion = 0.1; NumSides = 3*3*4; $fn = NumSides; CartridgeOA = [21.0,29.5,101.4]; // must match SVG pretty closely PrimerOA = [2.0,8.0,2.0]; CapsuleTip = [7.5,7.5,5.0]; Capsule = [7.5,18.8 + HoleWindage,83]; SeatingDepth = 25.0; Void = [CartridgeOA[ID]- 4.0,CartridgeOA[OD]- 4.0,CartridgeOA[LENGTH] – SeatingDepth – 4*PrimerOA[LENGTH]]; //———- // Define shapes module Cartridge() { difference() { rotate_extrude() import("Cartridge – 20x102mm outline.svg",layer="Cartridge Aligned Half"); up(PrimerOA[LENGTH]) cyl(PrimerOA[LENGTH] + Protrusion,d=PrimerOA[OD],anchor=TOP); up(CartridgeOA[LENGTH] + CapsuleTip[LENGTH]) cyl(SeatingDepth,d=Capsule[OD],anchor=TOP); up(CartridgeOA[LENGTH] – SeatingDepth) cyl(Void[LENGTH],d=CapsuleTip[OD],anchor=BOTTOM); if (Powder) { up(Void[LENGTH]/2) cyl(Void[LENGTH],d=CapsuleTip[OD],anchor=BOTTOM); up(2*PrimerOA[LENGTH]) cyl(Void[LENGTH],d=Void[OD],rounding=Void[OD]/2,anchor=BOTTOM); down(Protrusion) cyl(Void[LENGTH],d=PrimerOA[ID],anchor=BOTTOM); } } } module Primer() { difference() { cyl(PrimerOA[LENGTH] – Protrusion,d=PrimerOA[OD] – HoleWindage,anchor=BOTTOM); up(PrimerOA[LENGTH]) spheroid(d=PrimerOA[ID]); } } //———- // Build things if (Layout == "Show") //render() difference() { Cartridge(); cuboid(3*CartridgeOA[LENGTH],anchor=LEFT+BACK); } if (Layout == "Build") { Cartridge(); right(CartridgeOA[OD]) Primer(); }