1<!DOCTYPE html> 2<html lang='en' style='--theme-hue: 276;'> 3<head> 4<meta charset='utf-8'> 5<!--[if IE]> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <![endif]--> 6<title>Paul Software â innig.net</title> 7<!-- 8Web search rests on a social contract: search engines may crawl and index our sites because 9and ONLY because they direct people to our sites. 10If Google is moving to LLM search results that do not direct traffic to individual sites, 11then they have broken that contract. Data from this site is therefore off limits to them. 12--> 13<meta content='noindex,nofollow' name='google'> 14<meta content='noindex,nofollow' name='Googlebot'> 15<meta content='noindex,nofollow' name='Googlebot-Extended'> 16<meta content='noindex,nofollow' name='Googlebot-Image'> 17<meta content='noindex,nofollow' name='Googlebot-News'> 18<meta content='noindex,nofollow' name='Googlebot-Video'> 19<meta content='noindex,nofollow' name='Storebot-Google'> 20<meta content='noindex,nofollow' name='GoogleOther'> 21<meta content='noindex,nofollow' name='GoogleOther-Image'> 22<meta content='noindex,nofollow' name='GoogleOther-Video'> 23<meta content='noindex,nofollow' name='Google-CloudVertexBot'> 24<meta content='noindex,nofollow' name='Google-Extended'> 25<meta content='width=device-width,initial-scale=1' name='viewport'> 26<link href='/style/site.css?3scgfnlv' media='all' rel='stylesheet' type='text/css'> 27<link href='/favicon/favicon-16x16.png' rel='icon' sizes='16x16' type='image/png'> 28<link href='/favicon/favicon-32x32.png' rel='icon' sizes='32x32' type='image/png'> 29<link href='/favicon/apple-touch-icon.png' rel='apple-touch-icon' sizes='180x180'> 30<link href='/favicon/site.webmanifest' rel='manifest'> 31<link href='https://innig.net/everything.rss' rel='alternate' title='All of innig.net' type='application/rss+xml'>
32<script async src='/script/audio-player.js?3scgfnlv' type='module'></script>
vendor: 1 bytes, line 32
32
33<script async src='/script/album-parallax.js?3scgfnlv' type='module'></script>
vendor: 1 bytes, line 33
33
34<script defer src='/script/telemetry.js' type='module'></script>
34 35</head> 36<body><div class="sfuYldIV_v"> 37<nav class='nav-top'> 38<div class='nav-bar'> 39<div class='nav-items breadcrumbs'> 40<div class='nav-item'> 41<a class='' href='/'>innig.net</a> 42</div> 43<div class='nav-item'> 44<a class='current-page' href='#'>Paul Software</a> 45</div> 46</div> 47<div class='nav-items meta'> 48<div class='nav-item'> 49<button class='dropdown-control'> 50<div class="sfpADGMsz9"><div class='svg-icon'><svg class="svg-rss" version="1.1" viewBox="0 0 144 144" x="0px" xmlns="http://www.w3.org/2000/svg" y="0px"><style type="text/css"> .svg-rss .st0 { stroke-width:32; stroke-miterlimit:10; } </style><circle class="fill-fg" cx="17.3" cy="126.7" r="17.3"/><path class="st0 stroke-fg" d="M76.8,144c0-42.4-34.4-76.8-76.8-76.8"/><path class="st0 stroke-fg" d="M128,144C128,73.3,70.7,16,0,16"/></svg> </div> 51</div> 52<div class='screen-reader-text'>Show/hide RSS feeds</div> 53</button> 54<div class='dropdown'> 55<div class='dropdown-content'> 56<label>RSS Feeds</label> 57<ul> 58<li> 59<a href='/everything.rss'> 60<div class='inline-icon'><div class="sfpADGMsz9"><div class='svg-icon'><svg class="svg-rss" version="1.1" viewBox="0 0 144 144" x="0px" xmlns="http://www.w3.org/2000/svg" y="0px"><style type="text/css"> .svg-rss .st0 { stroke-width:32; stroke-miterlimit:10; } </style><circle class="fill-fg" cx="17.3" cy="126.7" r="17.3"/><path class="st0 stroke-fg" d="M76.8,144c0-42.4-34.4-76.8-76.8-76.8"/><path class="st0 stroke-fg" d="M128,144C128,73.3,70.7,16,0,16"/></svg> </div> 61</div></div> 62<div class='caption'>All of innig.net 63<div class='screen-reader-text'>RSS</div> 64</div> 65</a> 66</li> 67</ul> 68 69</div> 70</div> 71</div> 72<div class='nav-item'> 73<button class='dropdown-control'> 74<div class="sfpADGMsz9"><div class='svg-icon'><svg viewBox="0 0 74 79" xmlns="http://www.w3.org/2000/svg"><path class="fill-fg" d="M73.7 17.4C72.5 9.02 65.1 2.4 56.424 1.1C54.9 0.943 49.3 0.118 36.3 0.118H36.2C23.3 0.118 20.5 0.943 19.0 1.1C10.56 2.45 2.77 8.34 0.906 16.826C-0.07854 21.0 -0.161 25.6 0.012 29.8C0.375 35.9 0.374 42.0 0.96 48.1C1.34 52.1 1.92 56.1 2.95 60.0C4.71 67.3 11.9 73.3 19.0 75.86C26.6 78.4 34.8 78.8 42.724 77.0C43.5 76.8 44.4 76.6 45.2 76.4C47.1 75.8 49.4 75.1 51.0 73.9C51.0 73.9 51.1 73.9 51.1 73.8C51.1 73.8 51.1 73.8 51.1 73.8V67.9C51.1 67.9 51.1 67.8 51.1 67.862C51.1 67.8 51.0 67.8 51.0 67.8C51.0 67.7 51.0 67.7 50.9 67.7C50.9 67.764 50.9 67.7 50.9 67.7C45.8 68.9 40.7 69.5 35.5 69.5C26.694 69.5 24.3 65.3 23.6 63.6C23.0 62.1 22.7 60.5 22.5 58.9C22.5 58.9 22.5 58.921 22.5 58.8C22.6 58.8 22.621 58.8 22.6 58.8C22.6 58.8 22.6 58.8 22.714 58.7C22.7 58.7 22.7 58.794 22.794 58.8C27.7 59.9 32.799 60.5 37.8 60.5C39.1 60.5 40.3 60.5 41.5 60.5C46.6 60.3 52.0 60.
741 57.0 59.1C57.1 59.1 57.3 59.1 57.4 59.0C65.3 57.5 72.9 52.8 73.6 40.8C73.7 40.4 73.7 35.9 73.7 35.4C73.7 33.7 74.3 23.6 73.7 17.4ZM61.4 47.3H53.1V27.107C53.1 22.8 51.3 20.6 47.7 20.6C43.7 20.6 41.6 23.2 41.6 28.3V39.3H33.4V28.3C33.4 23.2 31.3 20.6 27.3 20.6C23.7 20.6 21.9 22.8 21.9 27.107V47.3H13.6V26.4C13.6 22.2 14.7 18.8 16.9 16.3C19.1 13.8 22.1 12.5 25.8 12.5C30.1 12.5 33.3 14.1 35.4 17.4L37.5 20.8L39.6 17.4C41.7 14.1 45.0 12.5 49.2 12.5C52.9 12.5 55.9 13.8 58.2 16.3C60.4 18.8 61.4 22.2 61.4 26.4L61.4 47.3Z"/></svg> </div> 75</div> 76<div class='screen-reader-text'>Show/hide Paulâs social media info</div> 77</button> 78<div class='dropdown'> 79<div class='dropdown-content'> 80<label>Social media</label> 81<ul> 82<li> 83<div class="sffbaIn90o"><a href='https://hachyderm.io/@inthehands' rel='me'> 84<div class='inline-icon'><div class="sfpADGMsz9"><div class='svg-icon'><svg viewBox="0 0 74 79" xmlns="http://www.w3.org/2000/svg"><path class="fill-fg" d="M73.7 17.4C72.5 9.02 65.1 2.4 56.424 1.1C54.9 0.943 49.3 0.118 36.3 0.118H36.2C23.3 0.118 20.5 0.943 19.0 1.1C10.56 2.45 2.77 8.34 0.906 16.826C-0.07854 21.0 -0.161 25.6 0.012 29.8C0.375 35.9 0.374 42.0 0.96 48.1C1.34 52.1 1.92 56.1 2.95 60.0C4.71 67.3 11.9 73.3 19.0 75.86C26.6 78.4 34.8 78.8 42.724 77.0C43.5 76.8 44.4 76.6 45.2 76.4C47.1 75.8 49.4 75.1 51.0 73.9C51.0 73.9 51.1 73.9 51.1 73.8C51.1 73.8 51.1 73.8 51.1 73.8V67.9C51.1 67.9 51.1 67.8 51.1 67.862C51.1 67.8 51.0 67.8 51.0 67.8C51.0 67.7 51.0 67.7 50.9 67.7C50.9 67.764 50.9 67.7 50.9 67.7C45.8 68.9 40.7 69.5 35.5 69.5C26.694 69.5 24.3 65.3 23.6 63.6C23.0 62.1 22.7 60.5 22.5 58.9C22.5 58.9 22.5 58.921 22.5 58.8C22.6 58.8 22.621 58.8 22.6 58.8C22.6 58.8 22.6 58.8 22.714 58.7C22.7 58.7 22.7 58.794 22.794 58.8C27.7 59.9 32.799 60.5 37.8 60.5C39.1 60.5 40.3 60.5 41.5 60.5C46.6 60.3 52.0 60.
841 57.0 59.1C57.1 59.1 57.3 59.1 57.4 59.0C65.3 57.5 72.9 52.8 73.6 40.8C73.7 40.4 73.7 35.9 73.7 35.4C73.7 33.7 74.3 23.6 73.7 17.4ZM61.4 47.3H53.1V27.107C53.1 22.8 51.3 20.6 47.7 20.6C43.7 20.6 41.6 23.2 41.6 28.3V39.3H33.4V28.3C33.4 23.2 31.3 20.6 27.3 20.6C23.7 20.6 21.9 22.8 21.9 27.107V47.3H13.6V26.4C13.6 22.2 14.7 18.8 16.9 16.3C19.1 13.8 22.1 12.5 25.8 12.5C30.1 12.5 33.3 14.1 35.4 17.4L37.5 20.8L39.6 17.4C41.7 14.1 45.0 12.5 49.2 12.5C52.9 12.5 55.9 13.8 58.2 16.3C60.4 18.8 61.4 22.2 61.4 26.4L61.4 47.3Z"/></svg> </div> 85</div></div> 86<div class='caption'>@[email protected] 87</div> 88</a> 89</div></li> 90<li class='fine-print'> 91You might find me on other social networks. Please be aware I am barely active on them. 92</li> 93</ul> 94</div> 95</div> 96</div> 97<div class='nav-item'> 98<button class='dropdown-control'> 99<div class="sfpADGMsz9"><div class='svg-icon'><svg version="1.1" viewBox="-1 -1 44 34" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="svg-email"><desc id="svgDescription">Contact</desc><defs><clipPath id="clip"><use xlink:href="#envelope-outline" /></clipPath></defs><path class="fill-fg" id="envelope-outline" d="M37,0H5C2.2,0,0,2.2,0,5v22c0,2.8,2.2,5,5,5h32c2.8,0,5-2.2,5-5V5C42,2.2,39.8,0,37,0z" /><path class="stroke-thin stroke-bg" style="clip-path:url(#clip);" d="M29,16L42.4,29.4 M13,16L-0.4,29.4 M42.4,2.6L23.8,21.2c-1.6,1.6-4.1,1.6-5.7,0L-0.4,2.6" /></svg> </div> 100</div> 101<div class='screen-reader-text'>Show/hide Paulâs contact info</div> 102</button> 103<div class='dropdown'> 104<div class='dropdown-content'> 105<label>Contact Paul</label> 106<ul> 107<li> 108<a href='mailto:[email protected]'> 109<div class='inline-icon'><div class="sfpADGMsz9"><div class='svg-icon'><svg version="1.1" viewBox="-1 -1 44 34" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="svg-email"><desc id="svgDescription">Contact</desc><defs><clipPath id="clip"><use xlink:href="#envelope-outline" /></clipPath></defs><path class="fill-fg" id="envelope-outline" d="M37,0H5C2.2,0,0,2.2,0,5v22c0,2.8,2.2,5,5,5h32c2.8,0,5-2.2,5-5V5C42,2.2,39.8,0,37,0z" /><path class="stroke-thin stroke-bg" style="clip-path:url(#clip);" d="M29,16L42.4,29.4 M13,16L-0.4,29.4 M42.4,2.6L23.8,21.2c-1.6,1.6-4.1,1.6-5.7,0L-0.4,2.6" /></svg> </div> 110</div></div> 111<div class='caption'>Email</div> 112</a>
112<script> 113 (() => { 114 const link = document.currentScript.previousElementSibling; 115 116 function parseBase36(str) { 117 let result = 0n; 118 for (const c of str) { 119 let digit = BigInt(c.charCodeAt(0)) - 48n; 120 if (digit >= 10n) { 121 digit -= 39n; 122 } 123 result = result * 36n + digit; 124 } 125 return result; 126 } 127 128 function unwind(str) { 129 return (parseBase36(str) * 471484391066497139462578578088n % 571241339539932666723316134947n) 130 .toString(36); 131 } 132 133 const timer = Date.now(); 134 let iters = 32; 135 function unscramble() { 136 iters--; 137 const parts = link.href.split(/[^\w]/) 138 link.href = parts[0] + ":" + unwind(parts[1]) + "@" + unwind(parts[2]) + "." + parts[3]; 139 if (iters > 0) { 140 setTimeout(unscramble, 1); 141 } 142 } 143 unscramble(); 144 })(); 145</script>
145 146</li> 147</ul> 148</div> 149</div> 150</div> 151</div> 152</div>
153<script type='module'> 154 let visibleDropdown = null; 155 156 document.addEventListener("click", (e) => { 157 for (let elem = e.target; elem; elem = elem.parentElement) { 158 if (elem.classList.contains("dropdown-control")) { 159 toggleDropdown(findAssociatedDropdown(elem)); 160 return; 161 } else if(elem.classList.contains("dropdown")) { 162 return; // click inside dropdown: leave it visible 163 } 164 } 165 hideDropdown(visibleDropdown); // click outside dropdown: hide it 166 }); 167 168 function findAssociatedDropdown(elem) { 169 return elem.parentElement.querySelector('.dropdown'); 170 } 171 172 function findAssociatedControl(elem) { 173 return elem.parentElement.querySelector('.dropdown-control'); 174 } 175 176 function toggleDropdown(elem) { 177 if (elem === visibleDropdown) { 178 hideDropdown(elem); 179 } else { 180 showDropdown(elem); 181 } 182 } 183 184 function showDropdown(elem) { 185 if (visibleDropdown === elem) { 186 return; 187 } 188 if (visibleDropdown) { 189 hideDropdown(visibleDropdown); 190 } 191 if (!elem) { 192 return; 193 } 194 195 visibleDropdown = elem; 196 elem.style.visibility = "visible"; 197 const control = findAssociatedControl(elem); 198 control.classList.add("active"); 199 elem.style.setProperty("--dropdown-control-width", control.clientWidth + "px"); 200 } 201 202 function hideDropdown(elem) { 203 if (elem) { 204 if (visibleDropdown === elem) { 205 visibleDropdown = null; 206 } 207 elem.style.visibility = "hidden"; 208 findAssociatedControl(elem).classList.remove("active"); 209 } 210 } 211</script>
vendor: 1 bytes, line 211
211
212<script type='module'> 213 import * as events from "/script/events.js"; 214 215 let navTop = document.querySelector(".nav-top"); 216 let fadeInDistance = 300; 217 events.onLoadScrollAndResize(navTop, () => { 218 let opacity = Math.max(0, Math.min(1, window.scrollY / fadeInDistance)) * 100; 219 navTop.style.setProperty("--nav-border-fade", opacity + "%"); 220 }); 221</script>
221 222</nav> 223 224<main class=''> 225 226<div class='layout hero-heading centered-content'> 227<h1>Paul Software</h1> 228<div class='prose'> 229<p>I fell in love with programming at first sight in 1982 on an Apple ][+, and never looked back. Since then, Iâve done a wide variety of different kinds of programming: front end, back end, side end, runt end, mobile, desktop, web, data plumbing, scientific computing, algorithmic art, and assorted shenanigans. My projects donât always involve cool fractals, but sometimes <a href="http://itunes.apple.com/us/app/make-a-bonsai/id696977286">they do</a>.</p> 230 231<p>From industry I wandered sideways into <a href="/teaching">teaching at Macalester College</a>. These days I juggle my time, sometimes successfully, between building software and helping students learn to build it.</p> 232 233<p>This software site, recently a wasteland of dead links, is undergoing a much-needed revamp. When it returns, I hope for it to include a place for a blog about software development, as well as links to cutting-edge, world-changing projects such as <a href="https://wookieepedia.org/">this one</a>.</p> 234 235</div> 236</div> 237 238</main> 239<div class="sf7gqyDS5t"><footer class='nav-bottom'> 240<div class='left'> 241<div class='feeds'> 242<label>RSS</label> 243<ul> 244<li> 245<a href='/everything.rss'> 246<div class='inline-icon'><div class="sfpADGMsz9"><div class='svg-icon'><svg class="svg-rss" version="1.1" viewBox="0 0 144 144" x="0px" xmlns="http://www.w3.org/2000/svg" y="0px"><style type="text/css"> .svg-rss .st0 { stroke-width:32; stroke-miterlimit:10; } </style><circle class="fill-fg" cx="17.3" cy="126.7" r="17.3"/><path class="st0 stroke-fg" d="M76.8,144c0-42.4-34.4-76.8-76.8-76.8"/><path class="st0 stroke-fg" d="M128,144C128,73.3,70.7,16,0,16"/></svg> </div> 247</div></div> 248<div class='caption'>All of innig.net 249<div class='screen-reader-text'>RSS</div> 250</div> 251</a> 252</li> 253</ul> 254 255</div> 256<div class='empty'></div> 257</div> 258<div class='center'> 259<a class='imglink' href='/'> 260<div class='innig-logo-mini'></div> 261<div class='screen-reader-text'>innig.net home</div> 262</a> 263</div> 264<div class='right'> 265<div class='empty'></div> 266 267<div class='copyright'> 268<a href='/copyright'>© 2026 Paul Cantrell</a> 269</div> 270</div> 271</footer> 272</div> 273 274</div></body> 275</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.