<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://www.marciot.com/blog/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.marciot.com/blog/" rel="alternate" type="text/html" /><updated>2026-07-06T07:14:32+00:00</updated><id>https://www.marciot.com/blog/feed.xml</id><title type="html">MarcioT’s Tech Blog</title><subtitle>3D Printing, Graphics, Retrocomputing and JavaScript</subtitle><author><name>Marcio Teixeira</name></author><entry><title type="html">A Visual Catalog of Retro Macintosh Software</title><link href="https://www.marciot.com/blog/retrocomputing/2026/07/05/visual-mac-software-catalog.html" rel="alternate" type="text/html" title="A Visual Catalog of Retro Macintosh Software" /><published>2026-07-05T00:00:00+00:00</published><updated>2026-07-05T00:00:00+00:00</updated><id>https://www.marciot.com/blog/retrocomputing/2026/07/05/visual-mac-software-catalog</id><content type="html" xml:base="https://www.marciot.com/blog/retrocomputing/2026/07/05/visual-mac-software-catalog.html"><![CDATA[<h1 id="a-visual-catalog-of-retro-macintosh-software">A Visual Catalog of Retro Macintosh Software</h1>

<p>A picture might be worth a thousands words, but <span id="totalIcons"></span> icons
are a fun way to explore retro apps! Hover over any icon to see the file‘s name, the
country of origin, and the name of the media; then go to <a href="/blog/assets/2026/07/05/about.html">about page</a> to find links to
the original media at the Macintosh Garden!</p>

<div id="scrollingDiv">
  <div id="evenPage" class="page"></div>
  <div id="oddPage" class="page"></div>
</div>]]></content><author><name>Marcio Teixeira</name></author><category term="Retrocomputing" /><summary type="html"><![CDATA[A Visual Catalog of Retro Macintosh Software A picture might be worth a thousands words, but icons are a fun way to explore retro apps! Hover over any icon to see the file‘s name, the country of origin, and the name of the media; then go to about page to find links to the original media at the Macintosh Garden!]]></summary></entry><entry><title type="html">The Great Fattening of Pixel Art</title><link href="https://www.marciot.com/blog/retrocomputing/2026/06/26/the-fattening-of-pixel-art.html" rel="alternate" type="text/html" title="The Great Fattening of Pixel Art" /><published>2026-06-26T00:00:00+00:00</published><updated>2026-06-26T00:00:00+00:00</updated><id>https://www.marciot.com/blog/retrocomputing/2026/06/26/the-fattening-of-pixel-art</id><content type="html" xml:base="https://www.marciot.com/blog/retrocomputing/2026/06/26/the-fattening-of-pixel-art.html"><![CDATA[<h1 id="the-great-fattening-of-pixel-art">The Great Fattening of Pixel Art</h1>

<p>Despite the popularity of pixel art, modern web browsers have a flaw that have led to a rampant waste. Take this happy Macintosh icon I found on the web:</p>

<p><img src="happy_mac.png" alt="" class="shadow" /></p>

<p>In its original form, as designed by Susan Kare, this icon was 32×32 pixels. It was packed into a mere 128 bytes on disk and it looked sharp and beautiful. Today, this image of it weighs in at 18,271 bytes and is a bit blurry around the edges.</p>

<p>Search on the web for pixel art and you find plenty bloated by upscaling and then blurred by JPEG compression. Even after this, these images are much larger than they ought to be. I could not find a single icon that was exactly 32×32 pixels—all were engorged to several hundred pixels on a side!</p>

<h2 id="why-is-this-happening">Why is this happening?</h2>

<p>A 32×32 pixel icon such as <img src="system.png" alt="" /> is far too small for today’s displays, but when you try to upscale it using a browser’s the <code class="language-plaintext highlighter-rouge">width</code> and <code class="language-plaintext highlighter-rouge">height</code> attributes, you will discover browsers provide only two upscaling choices:</p>

<ul>
  <li>linear interpolation</li>
  <li>nearest-neighbor interpolation</li>
</ul>

<p>Neither is satisfactory for pixel art: linear filtering blurs the image, while nearest-neighbor introduces inconsistent pixel sizes and shimmering during resizing.</p>

<p>CSS hints at solutions, but they are mostly dead ends: the <code class="language-plaintext highlighter-rouge">image-rendering</code> property offers <code class="language-plaintext highlighter-rouge">crisp-edges</code>, said to be “best for pixel art or line drawings,” behaves identically to nearest-neighbor in every browser I tried; <code class="language-plaintext highlighter-rouge">pixelated</code> was either unsupported or behaved inconsistently.</p>

<h2 id="a-possible-solution">A possible solution</h2>

<p>I was tipped off to a solution in the video <a href="https://www.youtube.com/watch?v=d6tp43wZqps&amp;t=177s">Crafting a Better Shader for Pixel Art Upscaling</a> by t3ssel8r, itself inspired by Cole Cecil’s 2017 article <a href="https://colececil.dev/blog/2017/scaling-pixel-art-without-destroying-it">Scaling Pixel Art Without Destroying It</a>.</p>

<p>The technique was originally intended for game engines, and SDL3 even includes <code class="language-plaintext highlighter-rouge">SDL_SCALEMODE_PIXELART</code>, but it got me wondering whether the same ideas could be brought to the web.</p>

<p>As a proof of concept I created a custom HTML element named <code class="language-plaintext highlighter-rouge">&lt;pixel-art&gt;</code>.</p>

<p>Below is a comparison between native images and the custom element.</p>

<div class="resizable">

<img src="system.png" />

<img src="system.png" class="pixelated" />

<pixel-art src="system.png"></pixel-art>

<pixel-art src="system.png" fatbits=""></pixel-art>

</div>

<p>Resize the container using the handle in the lower-right corner.</p>

<p>Pay particular attention to the middle two examples. They may appear similar initially, but the nearest-neighbor version exhibits shimmering and irregular pixel widths at certain scales. The custom element preserves crisp edges while maintaining consistent pixel geometry.</p>

<p>The final example recreates the classic Macintosh “fatbits” view.</p>

<div class="resizable">

<pixel-art src="apple-mac.png" fatbits=""></pixel-art>
<pixel-art src="floppy.png"></pixel-art>
<pixel-art src="color-happy-mac.png"></pixel-art>
<pixel-art src="cursors.png" fatbits=""></pixel-art>
<pixel-art src="kare_steve_jobs_transparent.png" fatbits=""></pixel-art>
<pixel-art src="dogcow.png" fatbits=""></pixel-art>

</div>

<p>Unlike pre-scaled PNGs, these source images occupy only a few hundred bytes on disk—much closer to how software shipped in the Macintosh era.</p>

<h2 id="some-things-are-best-left-to-the-imagination">Some things are best left to the imagination…</h2>

<p>Having a custom element opens up some unthinkable possibilities beyond simple upscaling. For example, supplying <img src="cross-stitch.png" alt="" /> as a texture transforms the pixel art into cross-stitch:</p>

<div class="resizable huge">

<pixel-art src="apple-mac.png" texture="cross-stitch.png">
</pixel-art>

</div>

<p>Were he still around to see it, Steve Jobs would have had a <img src="dogcow.png" alt="dogcow" /> about this, something like this:</p>

<div class="resizable huge">

<pixel-art src="kare_steve_jobs_transparent.png" texture="dogcow.png">
</pixel-art>

</div>]]></content><author><name>Marcio Teixeira</name></author><category term="Retrocomputing" /><summary type="html"><![CDATA[The Great Fattening of Pixel Art Despite the popularity of pixel art, modern web browsers have a flaw that have led to a rampant waste. Take this happy Macintosh icon I found on the web: In its original form, as designed by Susan Kare, this icon was 32×32 pixels. It was packed into a mere 128 bytes on disk and it looked sharp and beautiful. Today, this image of it weighs in at 18,271 bytes and is a bit blurry around the edges. Search on the web for pixel art and you find plenty bloated by upscaling and then blurred by JPEG compression. Even after this, these images are much larger than they ought to be. I could not find a single icon that was exactly 32×32 pixels—all were engorged to several hundred pixels on a side! Why is this happening? A 32×32 pixel icon such as is far too small for today’s displays, but when you try to upscale it using a browser’s the width and height attributes, you will discover browsers provide only two upscaling choices: linear interpolation nearest-neighbor interpolation Neither is satisfactory for pixel art: linear filtering blurs the image, while nearest-neighbor introduces inconsistent pixel sizes and shimmering during resizing. CSS hints at solutions, but they are mostly dead ends: the image-rendering property offers crisp-edges, said to be “best for pixel art or line drawings,” behaves identically to nearest-neighbor in every browser I tried; pixelated was either unsupported or behaved inconsistently. A possible solution I was tipped off to a solution in the video Crafting a Better Shader for Pixel Art Upscaling by t3ssel8r, itself inspired by Cole Cecil’s 2017 article Scaling Pixel Art Without Destroying It. The technique was originally intended for game engines, and SDL3 even includes SDL_SCALEMODE_PIXELART, but it got me wondering whether the same ideas could be brought to the web. As a proof of concept I created a custom HTML element named &lt;pixel-art&gt;. Below is a comparison between native images and the custom element. Resize the container using the handle in the lower-right corner. Pay particular attention to the middle two examples. They may appear similar initially, but the nearest-neighbor version exhibits shimmering and irregular pixel widths at certain scales. The custom element preserves crisp edges while maintaining consistent pixel geometry. The final example recreates the classic Macintosh “fatbits” view. Unlike pre-scaled PNGs, these source images occupy only a few hundred bytes on disk—much closer to how software shipped in the Macintosh era. Some things are best left to the imagination… Having a custom element opens up some unthinkable possibilities beyond simple upscaling. For example, supplying as a texture transforms the pixel art into cross-stitch: Were he still around to see it, Steve Jobs would have had a about this, something like this:]]></summary></entry><entry><title type="html">Happy New Year from an Arduino!</title><link href="https://www.marciot.com/blog/electronics/2018/01/03/happy-new-year-from-an-arduino.html" rel="alternate" type="text/html" title="Happy New Year from an Arduino!" /><published>2018-01-03T00:00:00+00:00</published><updated>2018-01-03T00:00:00+00:00</updated><id>https://www.marciot.com/blog/electronics/2018/01/03/happy-new-year-from-an-arduino</id><content type="html" xml:base="https://www.marciot.com/blog/electronics/2018/01/03/happy-new-year-from-an-arduino.html"><![CDATA[<h1 id="happy-new-year-from-an-arduino">Happy New Year from an Arduino!</h1>

<p>I’ve published an <a href="https://www.instructables.com/id/Super-Fast-Analog-Voltages-From-Arduino">Instructable</a> that shows how to generate super fast analog voltages on an Arduino. Watch the whole video for a very special New Years message!</p>

<iframe src="https://www.youtube.com/embed/3Uw0HXeMNiE?si=Qdlit0j18DlawvcQ" width="560" height="315" title="Super Fast Analog Voltages From Arduino" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="">
</iframe>

<p>To find out how I did this, head over to <a href="https://www.instructables.com/id/Super-Fast-Analog-Voltages-From-Arduino/">Super Fast Analog Voltages From Arduino</a></p>]]></content><author><name>Marcio Teixeira</name></author><category term="Electronics" /><summary type="html"><![CDATA[Happy New Year from an Arduino!]]></summary></entry><entry><title type="html">R.I.P. Macintosh PowerBook 3400c (1997-2017)</title><link href="https://www.marciot.com/blog/retrocomputing/2017/07/01/r-i-p-macintosh-powerbook-4300c-1997-2017.html" rel="alternate" type="text/html" title="R.I.P. Macintosh PowerBook 3400c (1997-2017)" /><published>2017-07-01T00:00:00+00:00</published><updated>2017-07-01T00:00:00+00:00</updated><id>https://www.marciot.com/blog/retrocomputing/2017/07/01/r-i-p-macintosh-powerbook-4300c-1997-2017</id><content type="html" xml:base="https://www.marciot.com/blog/retrocomputing/2017/07/01/r-i-p-macintosh-powerbook-4300c-1997-2017.html"><![CDATA[<h1 id="rip-macintosh-powerbook-3400c-1997-2017">R.I.P. Macintosh PowerBook 3400c (1997-2017)</h1>

<figure class="floatLeft">
  <img src="powerbook3400.jpg" />  
  <figcaption>Wikipedia, Neale Monks</figcaption>
</figure>

<p>Yesterday my Apple Macintosh PowerBook 3400c died.</p>

<p>This happened just after I had loaded MacOS 8.6 on a 80x 2GB CompactFlash card I got off eBay. My hope was that a <a href="https://www.youtube.com/watch?v=aGxJDRZRT_0">solid state boot drive</a> would insure against future mechanical hard drive failures, something I often worry about with my vintage computers. In a cruel yet comical twist of faith, the very act that I hoped to preserve it seems to have killed it.</p>

<p>Mr. Murphy, you never fail to surprise!</p>

<p>This laptop served me well. In college, it was the first laptop I ever owned. Years later, its floppy drive and Ethernet allowed me to image and preserve my remaining vintage Macintosh floppy disks and transfer them into <a href="https://basilisk.cebix.net">Basillisk II</a> for posterity—later some of these same files would form the basis for my <a href="http://retroweb.maclab.org">Retroweb Vintage Computer Museum</a>.</p>

<p>With Ethernet, AppleTalk, a floppy drive, PCMCIA, CD-ROM and SCSI, the 3400c was a perfect bridge from the new to the old: with it as a gateway to my modern PC, I lend new life to my childhood Macintosh Plus, loading new-to-me software onto it from the Internet and even helping in the process of loading drivers for a SCSI Iomega Zip drive. The Mac Plus now outlives it and owes much to the 3400c that preceded it.</p>

<p>And so, PowerBook 3400c, with your many peripherals at your side, may you rest in pieces. You will be missed.</p>

<p><strong>UPDATE:</strong> It was the AC power brick! With a replacement Apple “yo-yo” power supply from eBay, the Mac is now undead!</p>]]></content><author><name>Marcio Teixeira</name></author><category term="Retrocomputing" /><summary type="html"><![CDATA[R.I.P. Macintosh PowerBook 3400c (1997-2017)]]></summary></entry><entry><title type="html">360° Panoramas of In-Game Graphics from “Titanic: Honor &amp;amp; Glory”</title><link href="https://www.marciot.com/blog/graphics/2017/06/28/360-panoramas-from-in-game-graphics-from-titanic-honor-glory.html" rel="alternate" type="text/html" title="360° Panoramas of In-Game Graphics from “Titanic: Honor &amp;amp; Glory”" /><published>2017-06-28T00:00:00+00:00</published><updated>2017-06-28T00:00:00+00:00</updated><id>https://www.marciot.com/blog/graphics/2017/06/28/360-panoramas-from-in-game-graphics-from-titanic-honor-glory</id><content type="html" xml:base="https://www.marciot.com/blog/graphics/2017/06/28/360-panoramas-from-in-game-graphics-from-titanic-honor-glory.html"><![CDATA[<h1 id="360-panoramas-of-in-game-graphics-from-titanic-honor--glory">360° Panoramas of In-Game Graphics from “Titanic: Honor &amp; Glory”</h1>

<p>Recently I have had the opportunity to try out <a href="http://www.titanichg.com/demo/">third demo</a> of “Titanic: Honor &amp; Glory”, a gorgeous game under development by Vintage Digital Revival that recreates the historic ocean liner in stunning detail and realism.</p>

<p>I captured some 360° panoramas from the demo that show you just how realistic and gorgeous the graphics are. Here is a black and white panorama of the first class dining room:</p>

<p><img src="2017-06-24-07-52_stitch-3.jpg" alt="2017.06.24-07.52_stitch-3" /></p>

<p>And here is an exterior shot, showing the Titanic in its dock:</p>

<p><img src="thg-2017-06-24-05-38_stitch.jpg" alt="thg-2017.06.24-05.38_stitch" /></p>

<p>I highly recommend you try out the <a href="http://www.titanichg.com/demo/">demo yourself</a>. If you would like to create your own panoramas, I have made a YouTube video to document the process. The process should work for any game with a first person vantage point.</p>

<iframe width="560" height="315" src="https://www.youtube.com/embed/dzV2Zp2JSGI?si=R3IiuVd5YfOfJB0S" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<p>The following software was used in the video tutorial:</p>

<ul>
  <li><a href="http://www.titanichg.com/demo/">Titanic: Honor &amp; Glory Demo 3</a> (free)</li>
  <li><a href="https://www.microsoft.com/en-us/research/product/computational-photography-applications/image-composite-editor/">Image Composite Editor</a> (free)</li>
  <li><a href="http://panoramaphotographer.com/software/exiffixer/">Exif Fixer</a> (shareware)</li>
</ul>

<hr />

<p><strong><em>Advanced Tip:</em></strong> The method can also be extended to create equirectangular images, such as this:</p>

<p><img src="2017-06-24-11-32_stitch.jpg" alt="2017.06.24-11.32_stitch" /></p>

<p>The only modification to the technique described in the video is that the in-game footage is captured by sweeping from top to bottom while rotating in a spiral pattern.</p>]]></content><author><name>Marcio Teixeira</name></author><category term="Graphics" /><summary type="html"><![CDATA[360° Panoramas of In-Game Graphics from “Titanic: Honor &amp; Glory”]]></summary></entry><entry><title type="html">The Journey and Technology Behind “Maze War VR”</title><link href="https://www.marciot.com/blog/retrocomputing/2017/02/10/the-journey-and-technology-behind-maze-war-vr.html" rel="alternate" type="text/html" title="The Journey and Technology Behind “Maze War VR”" /><published>2017-02-10T00:00:00+00:00</published><updated>2017-02-10T00:00:00+00:00</updated><id>https://www.marciot.com/blog/retrocomputing/2017/02/10/the-journey-and-technology-behind-maze-war-vr</id><content type="html" xml:base="https://www.marciot.com/blog/retrocomputing/2017/02/10/the-journey-and-technology-behind-maze-war-vr.html"><![CDATA[<h1 id="the-journey-and-technology-behind-maze-war-vr">The Journey and Technology Behind “Maze War VR”</h1>

<p><img src="icon-512.png" alt="icon-512.png" class="dropCap" />
I have been working on a game that bridges the new to the old. <strong>Maze War</strong> is a historic game: it was <a href="http://www.polygon.com/features/2015/5/21/8627231/the-first-first-person-shooter">the very first first person shooter game</a>, released in 1974 for a computer called the Imlac.</p>

<p><strong>Maze War VR</strong> is a web game. On the surface, it is an simple web game with a retro feel, but how the first led to the second is a story of how one hack led to another, and then to another, and to many others still.</p>

<h2 id="it-started-with-a-museum">It Started with a Museum</h2>

<p>I have a sweet spot in my heart for retro computers and software. I built an <a href="http://retroweb.maclab.org/">online computer museum</a>, in JavaScript, so that people could interact with vintage computers and learn about them. This is the altruistic reason I share with others: in truth, the project was a selfish excuse for me to spend time tinkering with emulators, old software and Javascript, three things I really enjoy.</p>

<p>Some of the features in my museum stem from a desire to leave no “what if” unexplored. For instance: I made it so the emulated computers could talk to one another, via an emulated serial connection, from one browser window to another. This entailed a series of sub-hacks, including code to emulate a Hayes smart-modem and a virtual telephone exchange complete with made up telephone numbers in the <a href="https://en.wikipedia.org/wiki/555_(telephone_number)">ficticious 555 area code</a>, which would allow one emulated computers to “dial” another via WebRTC data-channels.</p>

<p>One of my early experiments involved running two instances of Battlechess, one on an emulated Macintosh and another on an emulated Commodora Amiga 500 in different browser tabs. Using my emulated modem, I was able to send a move across the line, but not quite reliably enough for a playable game (perhaps Battlechess required serial flow control, which my code did not implement).</p>

<figure>
  <img src="battlechess1.png" />  
  <figcaption>Battlechess on the Macintosh (left), Battlechess on the Amiga 500 (right)</figcaption>
</figure>

<p>MazeWars+ between two emulated Macintosh instances, however, worked perfectly, proving the concept and leading me to want even more than two simultaneous players.</p>

<h2 id="from-emulated-modems-to-localtalk">From Emulated Modems to LocalTalk</h2>

<p>Having gotten a pair of computers to play Maze Wars+ via a serial link, I set my sights on allowing several to do so. Most of the computers in my museum lacked native networking capabilities, but the Apple Macintosh Plus, the crown jewel of my museum, had in its original form the ability to participate in a LAN called a <a href="https://archive.org/details/networks2">LocalTalk network</a>. With LocalTalk, Maze Wars+ on the Macintosh supported up to thirty simultaneous players. Thirty is a whole lot better than two.</p>

<p><img src="mazewarsadlarge.jpg" alt="mazewarsadlarge" /></p>

<p>The Macintosh had a Mini DIN-9 port on its rear called the LocalTalk port. This port served double-duty: it could serve as a serial port or as an interface to the LocalTalk network, via Y-splitters. At minimum, two Macintosh computers could be cabled directly together via their LocalTalk ports, making a small LAN of two, but more often, several computers were daisy chained together into a larger network.</p>

<p>This simplicity lead me to believe that LocalTalk was plain old serial communications, with the transmit and receive lines electrically coupled into a common wire. I suspected that expanding my serial port demo to a LAN would merely involve shuttling serial data from one emulated Macintosh to several others, a trivial modification from sending it to just one, which I had already demonstrated. Easy peasy.</p>

<h2 id="the-zilog-e8530-scc">The Zilog E8530 SCC</h2>

<p>It turns out I was wrong. The Macintosh uses a chip called the Zilog E8530 SCC, a master-of-all-trades. Yes, it does serial communications, but it also has a much more complex mode called SDLC that forms the backbone of the LocalTalk network. In this mode, the chip is quite sophisticated. It frames packets, senses the media, avoids and detects collisions and computes frame checksums.</p>

<figure class="floatRight">
  <img src="zilog-e8530.jpg" />  
  <figcaption>The Z8530 SCC chip on on an Apple Macintosh 128K/512K motherboard</figcaption>
</figure>

<p>The <a href="http://www.hampa.ch/pce/">PCE emulator</a> I had been using implemented the serial mode of the Zilog chip, but not the SDLC mode. This task would fall to me. Fortunately, I was able to rely on the earlier work that had been done by <a href="http://mfort.net/Mini_vMac_LT/Mini_vMac_LT.html">Mike Fort for the Mini vMac emulator</a>. Even with that source as my guide and the comprehensive “<a href="http://www.zilog.com/force_download.php?filepath=YUhSMGNEb3ZMM2QzZHk1NmFXeHZaeTVqYjIwdlpHOWpjeTl6WlhKcFlXd3ZWVTB3TVRBNUxuQmtaZz09">Zilog SCC/ESCC User Manual</a>”, adding SDLC capabilities to the PCE emulator turned out to be a significant undertaking.</p>

<p>The reward came when I combined this with my earlier WebRTC work to allow several emulated Macintosh computers to communicate using native AppleTalk frames on a simulated inter-browser network. Today, my museum features <a href="http://retroweb.maclab.org/articles/Online-Games.html?emulator=pce-macplus">several multi-player Macintosh games</a>, such as NetTrek, Pararena, and Maze War+.</p>

<h2 id="the-xerox-alto-the-missing-link">The Xerox Alto: The Missing Link</h2>

<p><img src="alto-mazewar.jpg" alt="alto-mazewar" class="floatRight" /></p>

<p>My success in making Maze War+, a Macintosh game, playable in LAN mode in my museum lead me to the next step: a step forward but also back in history. The Xerox Alto, a computer developed at the Xerox PARC Labs in the 1970s, is an important player in the history of the Macintosh: it inspired Steve Jobs and his team to develop the Macintosh GUI.</p>

<p>Not only did the Xerox Alto play this pivotal role to the Macintosh, it also ran a much earlier version of Maze War, which had been developed at MIT around 1976. The more I learned, the more I understood that the Xerox Alto could weave together two threads in my museum: the story of the Macintosh as well as the story of Maze War, the game.</p>

<p>At this point, it became inevitable that my museum had to one day have a Xerox Alto running Maze War, in multi-player mode.</p>

<h2 id="the-xerox-alto-in-javascript">The Xerox Alto, in JavaScript.</h2>

<p>So inspired in this new mission, I came across an emulator called SALTO and <a href="https://github.com/marciot/retroweb-salto-simulator-js">ported it to JavaScript</a> using the EMSCRIPTEN toolchain. Sadly, I found that it was too buggy and slow to be of use.</p>

<p>Further research led me to another emulator, <a href="https://github.com/livingcomputermuseum/ContrAlto">Contralto</a>, that was being developed by the <a href="http://www.livingcomputers.org/">Living Computers: Museum+Labs</a>. This emulator proved to be much more robust, but was written in C#, making my plans of using EMSCRIPTEN to port it to Javascript impossible. Porting it by hand appeared daunting, but another brave individual had already taken up the challenge.</p>

<p>I got in touch with the author of ContraltoJS, <a href="http://www.loomcom.com/contraltojs/">a JavaScript port of Contalto</a>, and began collaborating with him. The Ethernet emulation was something the author of ContraltoJS had not yet tackled, so I volunteered to help, hoping that my earlier work with PCE and SDLC would give me the right experience for the job. This turned out to be true and my earlier AppleTalk tunneling code morphed into <a href="https://github.com/marciot/retroweb-networking">a general library for tunneling legacy protocols over WebRTC</a>: now capable of both LocalTalk and the legacy Ethernet frames that the Xerox Alto employed.</p>

<p><img src="diagram.png" alt="diagram" /></p>

<p>I eventually demonstrated instances of ContraltoJS running in a LAN in the web browser, playing two multi-player games for the Xerox Alto: Battleship and Maze War.</p>

<h2 id="rethinking-maze-war-in-the-third-dimension">Rethinking Maze War, in the Third Dimension</h2>

<p>Along side my work with emulators, I had been learning THREE.js, a 3D graphics library for the web. It occurred to me one night that making a Maze War clone would be a excellent learning experience. Drawing mazes and spheres is fairly straightforward in THREE.js, after all.</p>

<p>Doing a remake of Maze War did not take me deep enough into the rabbit hole, however,so I decided my version would have to be <a href="https://www.youtube.com/watch?v=XXOH0z3Aki8&amp;t=9s">compatible with the Xerox Alto version</a>. This goal led me to reverse-engineer the legacy Ethernet frames of the original game as it ran under ContraltoJS. These packets were being wrapped and tunneled over WebRTC, so I ended up writing a packet inspection tool for my tunneling library.</p>

<p>The work of understanding the Maze War frames was aided by documentation on the <a href="http://bitsavers.trailing-edge.com/pdf/xerox/parc/techReports/CSL-79-10_Pup_An_Internetwork_Architecture_Jul79.pdf">PUP data frames</a> used by the Xerox Alto as well as by code fragments from the original Maze War that were graciously provided to me by the developer of ContraltoJS.</p>

<h2 id="the-last-frontier-virtual-reality">The Last Frontier: Virtual Reality</h2>

<p>The last step on this journey was rewriting the game to <a href="https://webvr.info/">WebVR specs</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/API/Gamepad_API/Using_the_Gamepad_API">Gamepad API</a>. By using <a href="https://crosswalk-project.org/">Crosswalk</a>, I was able to bring it to <a href="https://play.google.com/store/apps/details?id=com.marciot.mazewar_vr_free&amp;hl=en">mobile phone users</a>. Maze War VR is now available both on the Android store as well as online for all major VR headsets. It has been tested on Google Cardboard, GearVR, Oculus Rift, but should work on any WebVR capable browser. Give it a try. Invite your friends to step right into this historic game with you.</p>

<p>Oh, and don’t forget it is also <a href="https://www.youtube.com/watch?v=XXOH0z3Aki8">compatible with Maze War for the Xerox Alto</a>.</p>

<iframe width="560" height="315" src="https://www.youtube.com/embed/iemJnNFq3r4?si=sVLHyZlyd9dUixDR" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>]]></content><author><name>Marcio Teixeira</name></author><category term="Retrocomputing" /><summary type="html"><![CDATA[The Journey and Technology Behind “Maze War VR”]]></summary></entry><entry><title type="html">Legacy Peer-to-Peer Networking over WebRTC (Introduction)</title><link href="https://www.marciot.com/blog/retrocomputing/2016/09/14/legacy-peer-to-peer-networking-over-webrtc-introduction.html" rel="alternate" type="text/html" title="Legacy Peer-to-Peer Networking over WebRTC (Introduction)" /><published>2016-09-14T00:00:00+00:00</published><updated>2016-09-14T00:00:00+00:00</updated><id>https://www.marciot.com/blog/retrocomputing/2016/09/14/legacy-peer-to-peer-networking-over-webrtc-introduction</id><content type="html" xml:base="https://www.marciot.com/blog/retrocomputing/2016/09/14/legacy-peer-to-peer-networking-over-webrtc-introduction.html"><![CDATA[<h1 id="legacy-peer-to-peer-networking-over-webrtc-introduction">Legacy Peer-to-Peer Networking over WebRTC (Introduction)</h1>

<p><img src="mazewars-eye.png" alt="mazewars-eye" class="floatLeft" style="width:15vw" /></p>

<p>One of my long term ambitions for the <a href="http://retroweb.maclab.org">RetroWeb Vintage Computer Museum</a> was to allow the emulated computers to talk to one another. Last July, after learning about <a href="https://webrtc.org/">WebRTC </a>and <a href="http://peerjs.com/">PeerJS</a>, I managed to accomplish first contact. By tunneling serial data over WebRTC, I had an emulated Macintosh running one browser window playing BattleChess with an emulated Commodore Amiga running on another browser window; I then played MazeWars+ with a stranger I met on the MAME IRC channel, showing that playing games originally meant for modem play via the Internet instead, through WebRTC, was not only possible, but also rather fun!</p>

<p>By August, I had become determined to tunnel AppleTalk packets over WebRTC, which would allow MazeWars+ and other multi-player Macintosh games to be played with <a href="http://retroweb.maclab.org/articles/Online-Games.html">several players at once</a>. In subsequent posts of this series, I will explain what it took to get there.</p>]]></content><author><name>Marcio Teixeira</name></author><category term="Retrocomputing" /><summary type="html"><![CDATA[Legacy Peer-to-Peer Networking over WebRTC (Introduction)]]></summary></entry><entry><title type="html">Let’s use style sheets for THREE.js elements, please!</title><link href="https://www.marciot.com/blog/graphics/2016/04/17/lets-use-style-sheets-for-three-js-elements-please.html" rel="alternate" type="text/html" title="Let’s use style sheets for THREE.js elements, please!" /><published>2016-04-17T00:00:00+00:00</published><updated>2016-04-17T00:00:00+00:00</updated><id>https://www.marciot.com/blog/graphics/2016/04/17/lets-use-style-sheets-for-three-js-elements-please</id><content type="html" xml:base="https://www.marciot.com/blog/graphics/2016/04/17/lets-use-style-sheets-for-three-js-elements-please.html"><![CDATA[<h1 id="lets-use-style-sheets-for-threejs-elements-please">Let’s use style sheets for THREE.js elements, please!</h1>

<p>The vast majority of THREE.js tutorials contain boilerplate code that assumes the WebGL content will be stretched across the entire window. These examples do not work at all when you try to position the WebGL canvas relative to other elements on the page.</p>

<p>A better approach is to start with code that works in all cases and then use the style sheet to stretch your content fit the entire window (if that is what you want). Doing it right from the start makes it easier to change your mind later and makes your code more flexible.</p>

<p>With this is mind, I am presenting two examples of how to do exactly that.</p>

<h2 id="example-1-full-window-threejs-using-css">Example 1: Full window THREE.js using CSS</h2>

<p>The <a href="http://marciot.github.io/blog-demos/three-layout/margin.html">first example</a> looks very much like your typical THREE.js program: it shows a WebGL canvas that spans the entire window. The only unusual thing is the pastel background and the white margin.</p>

<p><img src="margin.png" alt="margin" /></p>

<p>Let’s walk through the <a href="https://github.com/marciot/blog-demos/tree/master/three-layout/margin.html">source code</a> to see how it is done. First, I make the <code class="language-plaintext highlighter-rouge">body</code> tag to take up the entire window. Doing this requires <a href="http://stackoverflow.com/questions/485827/css-100-height-with-padding-margin">a trick</a> where I peg each side of the <code class="language-plaintext highlighter-rouge">body</code> to the corresponding side of the browser window:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">body</span> <span class="p">{</span>
  <span class="nl">position</span><span class="p">:</span> <span class="nb">absolute</span><span class="p">;</span>
  <span class="nl">top</span><span class="p">:</span>      <span class="m">0</span><span class="p">;</span>
  <span class="nl">left</span><span class="p">:</span>     <span class="m">0</span><span class="p">;</span>
  <span class="nl">right</span><span class="p">:</span>    <span class="m">0</span><span class="p">;</span>
  <span class="nl">bottom</span><span class="p">:</span>   <span class="m">0</span><span class="p">;</span>
  <span class="nl">margin</span><span class="p">:</span>   <span class="m">3em</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Second, I set a margin of 3em units to create the white border around the page (you could remove this line if you don’t want a border).</p>

<p>The <code class="language-plaintext highlighter-rouge">body</code> element is the parent of the <code class="language-plaintext highlighter-rouge">canvas</code> element; with CSS, it is easy to tell the <code class="language-plaintext highlighter-rouge">canvas</code> to use all the space available to it in the parent (which is the size of the window minus the margin):</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">canvas</span> <span class="p">{</span>
  <span class="nl">width</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span>
  <span class="nl">height</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Later on (I have separated the positioning and formatting into two separate style sheets), I assign a background color to the page and the <code class="language-plaintext highlighter-rouge">canvas</code>:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">html</span> <span class="p">{</span>
  <span class="nl">background-color</span><span class="p">:</span> <span class="no">white</span><span class="p">;</span>
<span class="p">}</span>

<span class="nt">canvas</span> <span class="p">{</span>
  <span class="nl">background-color</span><span class="p">:</span> <span class="no">beige</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="example-2a-flowing-text-around-webgl-content">Example 2a: Flowing text around WebGL content</h2>

<p>The <a href="http://marciot.github.io/blog-demos/three-layout/wrap.html">second example</a> shows text flowing dynamically around WebGL content. This example shows the true power of combining generically written THREE.js code with style sheets.</p>

<p><img src="wrap1.png" alt="wrap" /></p>

<p>To do this, I make a change to the <a href="https://github.com/marciot/blog-demos/blob/master/three-layout/wrap.html">style sheet</a> to size and position the <code class="language-plaintext highlighter-rouge">canvas</code> element:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">canvas</span> <span class="p">{</span>
  <span class="nl">float</span><span class="p">:</span> <span class="nb">left</span><span class="p">;</span>
  <span class="nl">width</span><span class="p">:</span> <span class="m">50%</span><span class="p">;</span>
  <span class="nl">height</span><span class="p">:</span> <span class="m">50%</span><span class="p">;</span>
  <span class="nl">margin-right</span><span class="p">:</span> <span class="m">2em</span><span class="p">;</span>
  <span class="nl">margin-bottom</span><span class="p">:</span> <span class="m">1em</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I set the <code class="language-plaintext highlighter-rouge">canvas</code> to be half the width and height of the parent and I tell it to float to the left of the surrounding text; the right and bottom margin control the space between the WebGL content and the text around it.</p>

<p>Provided the JavaScript is written correctly (as I will discuss below), going from one layout to another requires only a small change to the style sheet, as the following example shows.</p>

<h2 id="example-2b-interactive-zoom-you-can-have-it-both-ways">Example 2b: Interactive Zoom! You can have it both ways!</h2>

<p>We can take things one step forward. Since the layout is now controlled directly by the style sheet, it only takes a couple extra CSS rules and a bit of JavaScript to give the user the ability to zoom in on the WebGL content:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">canvas</span><span class="nc">.fullscreen</span> <span class="p">{</span>
  <span class="nl">position</span><span class="p">:</span> <span class="nb">fixed</span><span class="p">;</span>
  <span class="nl">top</span><span class="p">:</span>      <span class="m">0</span><span class="p">;</span>
  <span class="nl">left</span><span class="p">:</span>     <span class="m">0</span><span class="p">;</span>
  <span class="nl">width</span><span class="p">:</span>    <span class="m">100%</span><span class="p">;</span>
  <span class="nl">height</span><span class="p">:</span>   <span class="m">100%</span><span class="p">;</span>
<span class="p">}</span>

<span class="nt">canvas</span><span class="nd">:hover</span> <span class="p">{</span>
  <span class="nl">cursor</span><span class="p">:</span>   <span class="n">zoom-in</span><span class="p">;</span>
<span class="p">}</span>

<span class="nt">canvas</span><span class="nc">.fullscreen</span><span class="nd">:hover</span> <span class="p">{</span>
  <span class="nl">cursor</span><span class="p">:</span>   <span class="n">zoom-out</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The JavaScript code to toggle the state is equally trivial and could be made even easier if you were <a>using JQuery</a>:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">toggleFullscreen</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">var</span> <span class="nx">canvas</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">webgl</span><span class="dl">"</span><span class="p">);</span>
  <span class="k">if</span><span class="p">(</span><span class="nx">canvas</span><span class="p">.</span><span class="nx">className</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">fullscreen</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">canvas</span><span class="p">.</span><span class="nx">removeAttribute</span><span class="p">(</span><span class="dl">"</span><span class="s2">class</span><span class="dl">"</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="nx">canvas</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="dl">"</span><span class="s2">class</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">fullscreen</span><span class="dl">"</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="nx">onWindowResize</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<p>For simplicity, <code class="language-plaintext highlighter-rouge">toggleFullscreen</code> assumes there is just one class, this code will <a href="http://stackoverflow.com/questions/5169017/how-to-remove-class-attribute-from-div">need adjustments</a> if there are additional classes used on the canvas. The call to <code class="language-plaintext highlighter-rouge">onWindowResize</code> is necessary to adjust the parameters on the renderer.</p>

<h2 id="what-makes-it-work">What makes it work?</h2>

<p>Making these examples work requires some changes to the typical THREE.js setup code. Look at the improved <a href="http://marciot.github.io/blog-demos/three-layout/three-layout.js">JavaScript code</a> to see these changes.</p>

<p>One key insight came from <a href="http://www.rioki.org/2015/04/19/threejs-resize-and-canvas.html">rioki</a>: rather than using the window’s width and height, I use clientWidth and clientHeight; these are the exact dimension of the canvas element as rendered on the screen, after all CSS rules are applied.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">canvas</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">webgl</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">camera</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">THREE</span><span class="p">.</span><span class="nx">PerspectiveCamera</span><span class="p">(</span> <span class="mi">70</span><span class="p">,</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">clientWidth</span> <span class="o">/</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">clientHeight</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1000</span><span class="p">);</span>
</code></pre></div></div>

<p>I also tell the renderer the specific canvas I want to use rather than letting it create a new one for me:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">renderer</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">THREE</span><span class="p">.</span><span class="nx">WebGLRenderer</span><span class="p">({</span><span class="na">alpha</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="na">canvas</span><span class="p">:</span> <span class="nx">canvas</span><span class="p">});</span>
<span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">clientWidth</span><span class="p">;</span>
<span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">clientHeight</span><span class="p">;</span>
<span class="nx">renderer</span><span class="p">.</span><span class="nx">setViewport</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">clientWidth</span><span class="p">,</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">clientHeight</span><span class="p">);</span>
</code></pre></div></div>

<p>Setting the <code class="language-plaintext highlighter-rouge">alpha</code> to true allows me to set the background color of the canvas element from the style sheet; setting the canvas height and width to the clientWidth and clientHeight allows the resolution of the canvas to match the size of the canvas as it appears on the screen—these same values are passed on to the THREE.js renderer object.</p>

<p>The “onResize” handler updates everything when the browser window is resized, based on the <em>new</em> clientWidth and clientHeight values of the canvas:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">onWindowResize</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">var</span> <span class="nx">canvas</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">webgl</span><span class="dl">'</span><span class="p">);</span>
  <span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">clientWidth</span><span class="p">;</span>
  <span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">clientHeight</span><span class="p">;</span>
  <span class="nx">renderer</span><span class="p">.</span><span class="nx">setViewport</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">clientWidth</span><span class="p">,</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">clientHeight</span><span class="p">);</span>
  <span class="nx">camera</span><span class="p">.</span><span class="nx">aspect</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">clientWidth</span> <span class="o">/</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">clientHeight</span><span class="p">;</span>
  <span class="nx">camera</span><span class="p">.</span><span class="nx">updateProjectionMatrix</span><span class="p">();</span>
  <span class="nx">render</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>]]></content><author><name>Marcio Teixeira</name></author><category term="Graphics" /><summary type="html"><![CDATA[Let’s use style sheets for THREE.js elements, please!]]></summary></entry><entry><title type="html">Using THREE.JS to render to SVG</title><link href="https://www.marciot.com/blog/graphics/2016/03/26/using-three-js-to-render-to-svg.html" rel="alternate" type="text/html" title="Using THREE.JS to render to SVG" /><published>2016-03-26T00:00:00+00:00</published><updated>2016-03-26T00:00:00+00:00</updated><id>https://www.marciot.com/blog/graphics/2016/03/26/using-three-js-to-render-to-svg</id><content type="html" xml:base="https://www.marciot.com/blog/graphics/2016/03/26/using-three-js-to-render-to-svg.html"><![CDATA[<h1 id="using-threejs-to-render-to-svg">Using THREE.JS to render to SVG</h1>

<p>I came across a blog post that demonstrates <a href="http://blog.felixbreuer.net/2014/08/05/using-threejs-to-create-vector-graphics-from-3d-visualizations.html">using THREE.js to create SVG images</a>. Since that demo was done in CoffeeScript, it took me a while to understand it and build an equivalent JavaScript <a href="http://marciot.github.io/blog-demos/three-to-svg/">demo</a> (and the <a href="https://github.com/marciot/blog-demos/tree/master/three-to-svg">source code</a>).</p>

<p>The SVGRenderer is undocumented in the THREE.js website and it requires a few extra files that are not a part of the standard THREE.js distribution. This post will help you pull the necessary parts together.</p>

<p>My demo is loosly based on this <a href="http://solutiondesign.com/blog/-/blogs/webgl-and-three-js-texture-mappi-1/">great THREE.js tutorial</a>. I modified it to show the WebGL output on the left-hand side and the SVG capture on the right-hand side. Clicking the arrow updates the SVG capture and shows the code for the SVG on the bottom of the page.</p>

<p>When you hover your mouse cursor over the right-hand side, the paths of the SVG will highlight in red. These correspond to triangles in the original THREE.js model.</p>

<p><img src="three-to-svg.png?w=300" alt="three-to-svg" class="centered" /></p>

<p>The nice thing about rendering THREE.js models as SVG is that the visible faces will become <code class="language-plaintext highlighter-rouge">path</code> elements in the DOM, allowing you to highlight them with a single style sheet rule:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">path</span><span class="nd">:hover</span> <span class="p">{</span>
  <span class="py">stroke</span><span class="p">:</span> <span class="no">red</span><span class="p">;</span>
  <span class="py">stroke-width</span><span class="p">:</span> <span class="m">2px</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This rule tells the web browser to stroke all <code class="language-plaintext highlighter-rouge">path</code> elements in a solid red line whenever the user hovers the mouse cursor over them.</p>

<h2 id="how-it-works">How it works:</h2>

<p>The demo uses the undocumented SVGRenderer object from THREE.js. The <code class="language-plaintext highlighter-rouge">SVGRenderer</code> object depends on another object called <code class="language-plaintext highlighter-rouge">Projector</code>. Neither are part of the official THREE.js build, so I grabbed the two source files from the “examples/js/renderer” directory of the THREE.js distribution from <a href="https://github.com/mrdoob/three.js/tree/master/examples/js/renderers">GitHub</a> and placed them in my “lib” directory.</p>

<p>When the user clicks the arrow, the SVG on the right side is updated using a new instance of the SVGRenderer object. Here is what the code looks like:</p>

<pre><code class="language-javascipt">var svgRenderer = new THREE.SVGRenderer();
svgRenderer.setClearColor(0xffffff);
svgRenderer.setSize(width,height);
svgRenderer.setQuality('high');
svgRenderer.render(scene,camera);
</code></pre>

<p>The SVGRenderer will store the SVG data in the <code class="language-plaintext highlighter-rouge">domElement</code> attribute of itself. In the following code fragment, I insert it into a parent DIV and remove the <code class="language-plaintext highlighter-rouge">width</code> and <code class="language-plaintext highlighter-rouge">height</code> attributes from the <code class="language-plaintext highlighter-rouge">svg</code> element so that I can scale it with style sheet rules.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">svgContainer</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">svgRenderer</span><span class="p">.</span><span class="nx">domElement</span><span class="p">);</span>
<span class="nx">svgRenderer</span><span class="p">.</span><span class="nx">domElement</span><span class="p">.</span><span class="nx">removeAttribute</span><span class="p">(</span><span class="dl">'</span><span class="s1">width</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">svgRenderer</span><span class="p">.</span><span class="nx">domElement</span><span class="p">.</span><span class="nx">removeAttribute</span><span class="p">(</span><span class="dl">'</span><span class="s1">height</span><span class="dl">'</span><span class="p">);</span>
</code></pre></div></div>

<p>The SVG source for the bottom panel comes from the <code class="language-plaintext highlighter-rouge">svgContainer.innerHTML</code> attribute (<code class="language-plaintext highlighter-rouge">domElement.outerHTML</code> would work too). I use a regular expression to break up the source into lines and then post it into the destination text field:</p>

<pre><code class="language-javascipt">document.getElementById('source').value = svgContainer.innerHTML.replace(/&lt;path/g, "\n&lt;path");
</code></pre>]]></content><author><name>Marcio Teixeira</name></author><category term="Graphics" /><summary type="html"><![CDATA[Using THREE.JS to render to SVG]]></summary></entry><entry><title type="html">Could textures be used in 3D Printing?</title><link href="https://www.marciot.com/blog/3d%20printing/2016/03/24/could-textures-be-used-in-3d-printing.html" rel="alternate" type="text/html" title="Could textures be used in 3D Printing?" /><published>2016-03-24T00:00:00+00:00</published><updated>2016-03-24T00:00:00+00:00</updated><id>https://www.marciot.com/blog/3d%20printing/2016/03/24/could-textures-be-used-in-3d-printing</id><content type="html" xml:base="https://www.marciot.com/blog/3d%20printing/2016/03/24/could-textures-be-used-in-3d-printing.html"><![CDATA[<h1 id="could-textures-be-used-in-3d-printing">Could textures be used in 3D Printing?</h1>

<p>Real world objects have surface texture. In computer graphics, real world materials are simulated by combining bitmapped textures with facet information. Bump maps or displacement maps add fine details to a surface without increasing the polygon count.</p>

<p><img src="cube.png" alt="cube" class="floatLeft" /></p>

<p>3D printing software today does not use textures at all: a model of brick wall, for example, requires many very small polygons to capture details.</p>

<p>One reason is that 3D slicers use STL files which cannot carry texture information. There are several other formats that do, however, so using another file format such as OBJ would solve this problem. A second reason may be that 3D printing happens layer by layer, making it difficult to perform a displacement for all surface orientations.</p>

<p>For vertical surfaces, it would be easy: the slicer would use texture values to displace the print head laterally, perpendicular to the wall contours. For horizontal surfaces, however, the displacement would be above or below the current layer, which would complicate things.</p>

<p>In future posts, I’ll continue to explore the possibility of using textures in 3D printing and will present some work I am doing towards that end.</p>]]></content><author><name>Marcio Teixeira</name></author><category term="3D Printing" /><summary type="html"><![CDATA[Could textures be used in 3D Printing?]]></summary></entry></feed>