<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://web.dev/</id>
  <title>Michael Chang on web.dev</title>
  <updated>2026-04-15T23:21:06Z</updated>
  <author>
    <name>Michael Chang</name>
  </author>
  <link href="https://web.dev/authors/michaelchang/feed.xml" rel="self"/>
  <link href="https://web.dev/"/>
  <icon>https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZgEOjGWE13Mq77PmQuy2.png?auto=format</icon>
  <logo>https://web.dev/images/shared/rss-banner.png</logo>
  <subtitle>Developer</subtitle>
  
  
  <entry>
    <title>Making 100,000 Stars</title>
    <link href="https://web.dev/100000stars/"/>
    <updated>2012-11-28T00:00:00Z</updated>
    <id>https://web.dev/100000stars/</id>
    <content type="html" mode="escaped">&lt;p&gt;Hello! My name is Michael Chang and I work with the Data Arts Team at Google. Recently, we completed &lt;a href=&quot;http://workshop.chromeexperiments.com/stars&quot; rel=&quot;noopener&quot;&gt;100,000 Stars&lt;/a&gt;, a &lt;a href=&quot;http://chromeexperiments.com/&quot; rel=&quot;noopener&quot;&gt;Chrome Experiment&lt;/a&gt; visualizing nearby stars. The project was built with &lt;a href=&quot;https://github.com/mrdoob/three.js/&quot; rel=&quot;noopener&quot;&gt;THREE.js&lt;/a&gt; and CSS3D. In this case study I will outline the discovery process, share some programming techniques, and finish with some thoughts for future improvement.&lt;/p&gt;
&lt;p&gt;The topics discussed here will be fairly broad, and require some knowledge of THREE.js, though it is my hope that you can still enjoy this as a technical post-mortem. Feel free to jump to an area of interest using the table of contents button on the right. First, I&#39;ll show the rendering portion of the project, followed by shader management, and finally how to use CSS text labels in combination with WebGL.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;100,000 Stars, a Chrome Experiment by Data Arts Team&quot; decoding=&quot;async&quot; height=&quot;235&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 500px) 500px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/vlT6adEn3dy1cqmQVxBG.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/vlT6adEn3dy1cqmQVxBG.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/vlT6adEn3dy1cqmQVxBG.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/vlT6adEn3dy1cqmQVxBG.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/vlT6adEn3dy1cqmQVxBG.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/vlT6adEn3dy1cqmQVxBG.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/vlT6adEn3dy1cqmQVxBG.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/vlT6adEn3dy1cqmQVxBG.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/vlT6adEn3dy1cqmQVxBG.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/vlT6adEn3dy1cqmQVxBG.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/vlT6adEn3dy1cqmQVxBG.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/vlT6adEn3dy1cqmQVxBG.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/vlT6adEn3dy1cqmQVxBG.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/vlT6adEn3dy1cqmQVxBG.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/vlT6adEn3dy1cqmQVxBG.png?auto=format&amp;w=1000 1000w&quot; width=&quot;500&quot; /&gt;
    &lt;figcaption&gt;100,000 Stars uses THREE.js to visualize nearby stars in the Milky Way&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;discovering-space&quot;&gt;Discovering Space &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/100000stars/#discovering-space&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Shortly after we finished &lt;a href=&quot;http://www.chromeexperiments.com/detail/arms-globe/&quot; rel=&quot;noopener&quot;&gt;Small Arms Globe&lt;/a&gt;, I was experimenting with a THREE.js particle demo with depth of field. I noticed that I could change the interpreted &amp;quot;scale&amp;quot; of the scene by adjusting the amount of the effect applied. When the depth of field effect was really extreme, distant objects became really blurry similar to the way tilt-shift photography works on giving one the illusion of looking at a microscopic scene. Conversely, turning down the effect made it appear as if you were staring into deep space.&lt;/p&gt;
&lt;p&gt;I began hunting for data I could use to inject particle positions with, a path that lead me to &lt;a href=&quot;http://astronexus.com/&quot; rel=&quot;noopener&quot;&gt;astronexus.com&lt;/a&gt;&#39;s HYG database, a compilation of the three data sources (Hipparcos, Yale Bright Star Catalog, and Gliese/Jahreiss Catalog) accompanied by pre-calculated xyz Cartesian coordinates. Let&#39;s begin!&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Plotting star data.&quot; decoding=&quot;async&quot; height=&quot;264&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 500px) 500px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3ViDnDe2KbPrITxGzGK5.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3ViDnDe2KbPrITxGzGK5.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3ViDnDe2KbPrITxGzGK5.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3ViDnDe2KbPrITxGzGK5.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3ViDnDe2KbPrITxGzGK5.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3ViDnDe2KbPrITxGzGK5.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3ViDnDe2KbPrITxGzGK5.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3ViDnDe2KbPrITxGzGK5.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3ViDnDe2KbPrITxGzGK5.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3ViDnDe2KbPrITxGzGK5.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3ViDnDe2KbPrITxGzGK5.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3ViDnDe2KbPrITxGzGK5.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3ViDnDe2KbPrITxGzGK5.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3ViDnDe2KbPrITxGzGK5.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3ViDnDe2KbPrITxGzGK5.png?auto=format&amp;w=1000 1000w&quot; width=&quot;500&quot; /&gt;
    &lt;figcaption&gt;The first step is to plot every star in the catalog as a single particle.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
    &lt;img alt=&quot;The named stars.&quot; decoding=&quot;async&quot; height=&quot;253&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 500px) 500px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QI23eDp67VmKRHeOB7w9.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QI23eDp67VmKRHeOB7w9.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QI23eDp67VmKRHeOB7w9.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QI23eDp67VmKRHeOB7w9.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QI23eDp67VmKRHeOB7w9.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QI23eDp67VmKRHeOB7w9.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QI23eDp67VmKRHeOB7w9.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QI23eDp67VmKRHeOB7w9.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QI23eDp67VmKRHeOB7w9.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QI23eDp67VmKRHeOB7w9.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QI23eDp67VmKRHeOB7w9.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QI23eDp67VmKRHeOB7w9.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QI23eDp67VmKRHeOB7w9.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QI23eDp67VmKRHeOB7w9.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QI23eDp67VmKRHeOB7w9.png?auto=format&amp;w=1000 1000w&quot; width=&quot;500&quot; /&gt;
    &lt;figcaption&gt;Some stars in the catalog have proper names, labeled here.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;It took about an hour to hack together something that placed the star data in 3D space. There are exactly 119,617 stars in the data set, so representing each star with a particle is not a problem for a modern GPU. There are also 87 individually identified stars, so I created a CSS marker overlay using the same technique I &lt;a href=&quot;http://mflux.tumblr.com/post/28367579774/armstradeviz&quot; rel=&quot;noopener&quot;&gt;described&lt;/a&gt; in Small Arms Globe.&lt;/p&gt;
&lt;p&gt;During this time I had just finished the &lt;a href=&quot;http://masseffect.bioware.com/&quot; rel=&quot;noopener&quot;&gt;Mass Effect&lt;/a&gt; series. In the game the player is invited to explore the galaxy and &lt;a href=&quot;https://www.youtube.com/watch?v=1g92-pgyJrU&quot; rel=&quot;noopener&quot;&gt;scan various planets and read about&lt;/a&gt; their completely fictional, wikipedia-sounding history: what species had thrived on the planet, its geological history, and so forth.&lt;/p&gt;
&lt;p&gt;Knowing the wealth of actual data that&#39;s out there about stars, one could conceivably present real information about the galaxy in the same way. The ultimate goal for this project would be to bring to life this data, allow the viewer to explore the galaxy à la Mass Effect, to learn about stars and their distribution, and hopefully inspire a sense of awe and wonder about space. Phew!&lt;/p&gt;
&lt;p&gt;I should probably preface the rest of this case-study by saying that &lt;strong&gt;I am by no means an astronomer, and that this is the work of amateur research&lt;/strong&gt; supported by some advice from external experts. This project should definitely be construed as an artist interpretation of space.&lt;/p&gt;
&lt;h2 id=&quot;building-a-galaxy&quot;&gt;Building a Galaxy &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/100000stars/#building-a-galaxy&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;My plan was to procedurally generate a model of the galaxy that can put the star data in context -- and hopefully give an awesome view of our place in the Milky Way.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;An early prototype of the galaxy.&quot; decoding=&quot;async&quot; height=&quot;200&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 500px) 500px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/S5ZbLFPB3RnfRTOT35mW.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/S5ZbLFPB3RnfRTOT35mW.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/S5ZbLFPB3RnfRTOT35mW.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/S5ZbLFPB3RnfRTOT35mW.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/S5ZbLFPB3RnfRTOT35mW.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/S5ZbLFPB3RnfRTOT35mW.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/S5ZbLFPB3RnfRTOT35mW.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/S5ZbLFPB3RnfRTOT35mW.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/S5ZbLFPB3RnfRTOT35mW.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/S5ZbLFPB3RnfRTOT35mW.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/S5ZbLFPB3RnfRTOT35mW.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/S5ZbLFPB3RnfRTOT35mW.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/S5ZbLFPB3RnfRTOT35mW.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/S5ZbLFPB3RnfRTOT35mW.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/S5ZbLFPB3RnfRTOT35mW.png?auto=format&amp;w=1000 1000w&quot; width=&quot;500&quot; /&gt;
    &lt;figcaption&gt;An early prototype of the Milky Way particle system.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To generate the Milky Way, I spawned 100,000 particles and placed them in a spiral by emulating the way galactic arms are formed. I wasn&#39;t too worried about the specifics of spiral arm formation because this would be a representational model rather than a mathematical one. However I did try to get the number of spiral arms more or less correct, and spinning in the &amp;quot;right direction.&amp;quot;&lt;/p&gt;
&lt;p&gt;In later versions of the Milky Way model I de-emphasized the use of particles in favor of a planar image of a galaxy to accompany the particles, hopefully giving it a more of a photographic appearance. The &lt;a href=&quot;http://www.eso.org/public/images/ngc1232b/&quot; rel=&quot;noopener&quot;&gt;actual image is of spiral galaxy NGC 1232&lt;/a&gt; roughly 70 million light years away from us, image-manipulated to look like the Milky Way.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Figuring out the scale of the galaxy.&quot; decoding=&quot;async&quot; height=&quot;221&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 500px) 500px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dQZVXko0eZdufru9zpqr.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dQZVXko0eZdufru9zpqr.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dQZVXko0eZdufru9zpqr.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dQZVXko0eZdufru9zpqr.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dQZVXko0eZdufru9zpqr.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dQZVXko0eZdufru9zpqr.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dQZVXko0eZdufru9zpqr.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dQZVXko0eZdufru9zpqr.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dQZVXko0eZdufru9zpqr.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dQZVXko0eZdufru9zpqr.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dQZVXko0eZdufru9zpqr.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dQZVXko0eZdufru9zpqr.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dQZVXko0eZdufru9zpqr.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dQZVXko0eZdufru9zpqr.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dQZVXko0eZdufru9zpqr.png?auto=format&amp;w=1000 1000w&quot; width=&quot;500&quot; /&gt;
    &lt;figcaption&gt;Every GL unit is a light year. In this case the sphere is 110,000 light years wide, encompassing the particle system.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I decided early on to represent one GL unit, basically a pixel in 3D, as one light year -- a convention that unified placement for everything visualized, and unfortunately gave me serious precision issues later on.&lt;/p&gt;
&lt;p&gt;Another convention I decided was to rotate the entire scene rather than moving the camera, something I&#39;ve done in a few other projects. One advantage is that everything is placed onto a &amp;quot;turntable&amp;quot; so that mouse-dragging left and right rotates the object in question, but zooming in is only a matter of changing camera.position.z.&lt;/p&gt;
&lt;p&gt;Field of view (or FOV) for the camera is also dynamic. As one pulls outwards, the field of view widens, taking in more and more of the galaxy. The opposite is true when moving inwards towards a star, the field of view narrows. This allows the camera to view things that are infinitesimal (in comparison to the galaxy) by squishing the FOV down to something of a god-like magnifying glass without having to deal with near-plane clipping issues.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Different ways of rendering a galaxy.&quot; decoding=&quot;async&quot; height=&quot;366&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 500px) 500px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJhqkFVdJ81oTmVYsyFY.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJhqkFVdJ81oTmVYsyFY.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJhqkFVdJ81oTmVYsyFY.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJhqkFVdJ81oTmVYsyFY.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJhqkFVdJ81oTmVYsyFY.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJhqkFVdJ81oTmVYsyFY.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJhqkFVdJ81oTmVYsyFY.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJhqkFVdJ81oTmVYsyFY.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJhqkFVdJ81oTmVYsyFY.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJhqkFVdJ81oTmVYsyFY.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJhqkFVdJ81oTmVYsyFY.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJhqkFVdJ81oTmVYsyFY.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJhqkFVdJ81oTmVYsyFY.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJhqkFVdJ81oTmVYsyFY.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJhqkFVdJ81oTmVYsyFY.png?auto=format&amp;w=1000 1000w&quot; width=&quot;500&quot; /&gt;
    &lt;figcaption&gt;(above) Early particle galaxy. (below) Particles accompanied by an image plane.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;From here I was able to &amp;quot;place&amp;quot; the Sun at some number of units away from the galactic core. I was also able to visualize the relative size of the solar system by mapping out the radius of the &lt;a href=&quot;http://en.wikipedia.org/wiki/Kuiper_cliff#.22Kuiper_cliff.22&quot; rel=&quot;noopener&quot;&gt;Kuiper Cliff&lt;/a&gt; (I eventually chose to visualize the &lt;a href=&quot;http://en.wikipedia.org/wiki/Oort_cloud&quot; rel=&quot;noopener&quot;&gt;Oort Cloud&lt;/a&gt;) instead. Within this model solar system, I could also visualize a simplified orbit of Earth, and the actual radius of the Sun in comparison.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;The solar system.&quot; decoding=&quot;async&quot; height=&quot;267&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 500px) 500px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iVU66rpZ86Wfoz3mY7B6.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iVU66rpZ86Wfoz3mY7B6.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iVU66rpZ86Wfoz3mY7B6.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iVU66rpZ86Wfoz3mY7B6.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iVU66rpZ86Wfoz3mY7B6.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iVU66rpZ86Wfoz3mY7B6.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iVU66rpZ86Wfoz3mY7B6.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iVU66rpZ86Wfoz3mY7B6.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iVU66rpZ86Wfoz3mY7B6.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iVU66rpZ86Wfoz3mY7B6.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iVU66rpZ86Wfoz3mY7B6.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iVU66rpZ86Wfoz3mY7B6.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iVU66rpZ86Wfoz3mY7B6.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iVU66rpZ86Wfoz3mY7B6.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iVU66rpZ86Wfoz3mY7B6.png?auto=format&amp;w=1000 1000w&quot; width=&quot;500&quot; /&gt;
    &lt;figcaption&gt;The Sun orbited by planets and a sphere representing the Kuiper Belt.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The Sun was difficult to render. I had to cheat with as many real-time graphics techniques as I knew. The surface of the Sun is a hot froth of plasma and needed to pulse and change over time. This was simulated via a bitmap texture of an infrared image of the solar surface. The surface shader makes a color look-up based on the grayscale of this texture and performs a look-up in a separate color ramp. When this look-up is shifted over time it creates this lava-like distortion.&lt;/p&gt;
&lt;p&gt;A similar technique was used for the corona of the Sun, except that it would be a flat sprite card that always faces the camera using &lt;a href=&quot;https://github.com/mrdoob/three.js/blob/master/src/extras/core/Gyroscope.js&quot; rel=&quot;noopener&quot;&gt;https://github.com/mrdoob/three.js/blob/master/src/extras/core/Gyroscope.js&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Rendering Sol.&quot; decoding=&quot;async&quot; height=&quot;313&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 500px) 500px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4yVISJF05rsbqZBogwl4.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4yVISJF05rsbqZBogwl4.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4yVISJF05rsbqZBogwl4.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4yVISJF05rsbqZBogwl4.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4yVISJF05rsbqZBogwl4.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4yVISJF05rsbqZBogwl4.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4yVISJF05rsbqZBogwl4.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4yVISJF05rsbqZBogwl4.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4yVISJF05rsbqZBogwl4.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4yVISJF05rsbqZBogwl4.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4yVISJF05rsbqZBogwl4.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4yVISJF05rsbqZBogwl4.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4yVISJF05rsbqZBogwl4.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4yVISJF05rsbqZBogwl4.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4yVISJF05rsbqZBogwl4.png?auto=format&amp;w=1000 1000w&quot; width=&quot;500&quot; /&gt;
    &lt;figcaption&gt;Early version of the Sun.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The solar flares were created via vertex and fragment shaders applied to a torus, spinning just around the edge of the solar surface. The vertex shader has a noise function causing it to weave in a blob-like fashion.&lt;/p&gt;
&lt;p&gt;It was here that I was starting to experience some z-fighting issues due to GL precision. All of the variables for precision were pre-defined in THREE.js, so I couldn&#39;t realistically bump up the precision without a huge amount of work. Precision issues were not as bad near the origin. However, once I started modeling other star systems, this became an issue.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Star model.&quot; decoding=&quot;async&quot; height=&quot;324&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 500px) 500px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/U8H1iHnTmqlKLBLmwO75.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/U8H1iHnTmqlKLBLmwO75.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/U8H1iHnTmqlKLBLmwO75.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/U8H1iHnTmqlKLBLmwO75.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/U8H1iHnTmqlKLBLmwO75.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/U8H1iHnTmqlKLBLmwO75.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/U8H1iHnTmqlKLBLmwO75.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/U8H1iHnTmqlKLBLmwO75.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/U8H1iHnTmqlKLBLmwO75.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/U8H1iHnTmqlKLBLmwO75.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/U8H1iHnTmqlKLBLmwO75.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/U8H1iHnTmqlKLBLmwO75.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/U8H1iHnTmqlKLBLmwO75.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/U8H1iHnTmqlKLBLmwO75.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/U8H1iHnTmqlKLBLmwO75.png?auto=format&amp;w=1000 1000w&quot; width=&quot;500&quot; /&gt;
    &lt;figcaption&gt;The code for rendering the Sun was later generalized to render other stars.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;There were a few hacks I employed to mitigate z-fighting. THREE&#39;s &lt;a href=&quot;https://github.com/mrdoob/three.js/blob/master/src/materials/Material.js&quot; rel=&quot;noopener&quot;&gt;Material.polygonoffset&lt;/a&gt; is a property that allows polygons to be rendered at a different perceived location (as far as I understand). This was used to force the corona plane to always render on top of the Sun&#39;s surface. Below this, a Sun &amp;quot;halo&amp;quot; was rendered to give sharp light rays moving away from the sphere.&lt;/p&gt;
&lt;p&gt;A different problem related to precision was that the star models would begin jittering as the scene zoomed in. To fix this I had to &amp;quot;zero out&amp;quot; the scene rotation and separately rotate the star model and environment map to give the illusion that you are orbiting the star.&lt;/p&gt;
&lt;h3 id=&quot;creating-lensflare&quot;&gt;Creating Lensflare &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/100000stars/#creating-lensflare&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;
    &lt;img alt=&quot;With great power comes great responsibility.&quot; decoding=&quot;async&quot; height=&quot;324&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 500px) 500px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aS7ugNArmQTjwwmT1PeS.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aS7ugNArmQTjwwmT1PeS.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aS7ugNArmQTjwwmT1PeS.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aS7ugNArmQTjwwmT1PeS.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aS7ugNArmQTjwwmT1PeS.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aS7ugNArmQTjwwmT1PeS.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aS7ugNArmQTjwwmT1PeS.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aS7ugNArmQTjwwmT1PeS.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aS7ugNArmQTjwwmT1PeS.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aS7ugNArmQTjwwmT1PeS.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aS7ugNArmQTjwwmT1PeS.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aS7ugNArmQTjwwmT1PeS.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aS7ugNArmQTjwwmT1PeS.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aS7ugNArmQTjwwmT1PeS.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aS7ugNArmQTjwwmT1PeS.png?auto=format&amp;w=1000 1000w&quot; width=&quot;500&quot; /&gt;
    &lt;figcaption&gt;With great power comes great responsibility.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Space visualizations are where I feel like I can get away with excessive use of lensflare. &lt;a href=&quot;https://github.com/mrdoob/three.js/blob/master/src/extras/objects/LensFlare.js&quot; rel=&quot;noopener&quot;&gt;THREE.LensFlare&lt;/a&gt; serves this purposes, all I needed to do was throw in some anamorphic hexagons and a dash of &lt;a href=&quot;http://en.wikipedia.org/wiki/J._J._Abrams&quot; rel=&quot;noopener&quot;&gt;JJ Abrams&lt;/a&gt;. The snippet below shows how to construct them in your scene.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// This function retuns a lesnflare THREE object to be .add()ed to the scene graph&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;addLensFlare&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;z&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; size&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; overrideImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; flareColor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;THREE&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0xffffff&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;lensFlare &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;THREE&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;LensFlare&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; overrideImage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;700&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;THREE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AdditiveBlending&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; flareColor &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// we&#39;re going to be using multiple sub-lens-flare artifacts, each with a different size&lt;/span&gt;&lt;br /&gt;lensFlare&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; textureFlare1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4096&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;THREE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AdditiveBlending &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;lensFlare&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; textureFlare2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;512&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;THREE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AdditiveBlending &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;lensFlare&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; textureFlare2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;512&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;THREE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AdditiveBlending &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;lensFlare&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; textureFlare2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;512&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;THREE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AdditiveBlending &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// and run each through a function below&lt;/span&gt;&lt;br /&gt;lensFlare&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;customUpdateCallback &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; lensFlareUpdateCallback&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;lensFlare&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;position &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;THREE&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Vector3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;z&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;lensFlare&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;size &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; size &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; size &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16000&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; lensFlare&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// this function will operate over each lensflare artifact, moving them around the screen&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;lensFlareUpdateCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lensFlares&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; flare&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; vecX &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;positionScreen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;x _ &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; vecY &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;positionScreen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;y _ &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; size &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;size &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;size &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; camDistance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; camera&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;position&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; f &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; f &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;lt&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; fl&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; f &lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;flare &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lensFlares&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; f &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;flare&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;positionScreen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; vecX &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; flare&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;distance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;flare&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;y &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;positionScreen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;y &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; vecY &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; flare&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;distance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;flare&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scale &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; size &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; camDistance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;flare&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rotation &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;an-easy-way-to-do-texture-scrolling&quot;&gt;An easy way to do texture scrolling &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/100000stars/#an-easy-way-to-do-texture-scrolling&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Inspired by Homeworld.&quot; decoding=&quot;async&quot; height=&quot;247&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 500px) 500px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jKVWKP56RGV6ULZmY0LC.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jKVWKP56RGV6ULZmY0LC.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jKVWKP56RGV6ULZmY0LC.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jKVWKP56RGV6ULZmY0LC.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jKVWKP56RGV6ULZmY0LC.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jKVWKP56RGV6ULZmY0LC.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jKVWKP56RGV6ULZmY0LC.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jKVWKP56RGV6ULZmY0LC.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jKVWKP56RGV6ULZmY0LC.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jKVWKP56RGV6ULZmY0LC.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jKVWKP56RGV6ULZmY0LC.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jKVWKP56RGV6ULZmY0LC.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jKVWKP56RGV6ULZmY0LC.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jKVWKP56RGV6ULZmY0LC.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jKVWKP56RGV6ULZmY0LC.png?auto=format&amp;w=1000 1000w&quot; width=&quot;500&quot; /&gt;
    &lt;figcaption&gt;A cartesian plane to help with spatial orientation in space.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;For the &amp;quot;spatial orientation plane&amp;quot;, a gigantic THREE.CylinderGeometry() was created and centered on the Sun. To create the &amp;quot;wave of light&amp;quot; fanning outwards, I modified its texture offset over time like so:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;mesh&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;material&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;needsUpdate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;mesh&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;material&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onUpdate&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;offset&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;y &lt;span class=&quot;token operator&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.001&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;needsUpdate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;map&lt;/code&gt; is the texture belonging to the material, which get an onUpdate function you can over-write. Setting its offset causes the texture to be &amp;quot;scrolled&amp;quot; along that axis, and spamming needsUpdate = true would force this behavior to loop.&lt;/p&gt;
&lt;h3 id=&quot;using-color-ramps&quot;&gt;Using color ramps &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/100000stars/#using-color-ramps&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Each star has a different color based on a &amp;quot;color-index&amp;quot; that astronomers have assigned them. In general, red stars are cooler and blue/purple stars are hotter. A band of white and intermediate orange colors exist in this gradient.&lt;/p&gt;
&lt;p&gt;When rendering the stars I wanted to give each particle its own color based on this data. The way to do this was with &amp;quot;attributes&amp;quot; given to the shader material applied to the particles.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; shaderMaterial &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;THREE&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ShaderMaterial&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token literal-property property&quot;&gt;uniforms&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; datastarUniforms&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token literal-property property&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; datastarAttributes&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;_ &lt;span class=&quot;token char-set class-name&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token char-set class-name&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token char-set class-name&quot;&gt;.&lt;/span&gt; etc _&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; datastarAttributes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token literal-property property&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;f&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token literal-property property&quot;&gt;colorIndex&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;f&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Filling up the colorIndex array would give each particle its unique color in the shader. Normally one would pass in a color vec3, but in this instance I&#39;m passing in a float for the eventual color ramp look-up.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Color ramp.&quot; decoding=&quot;async&quot; height=&quot;10&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 500px) 500px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FalbN0I27j7vvmlJQQJj.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FalbN0I27j7vvmlJQQJj.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FalbN0I27j7vvmlJQQJj.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FalbN0I27j7vvmlJQQJj.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FalbN0I27j7vvmlJQQJj.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FalbN0I27j7vvmlJQQJj.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FalbN0I27j7vvmlJQQJj.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FalbN0I27j7vvmlJQQJj.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FalbN0I27j7vvmlJQQJj.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FalbN0I27j7vvmlJQQJj.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FalbN0I27j7vvmlJQQJj.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FalbN0I27j7vvmlJQQJj.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FalbN0I27j7vvmlJQQJj.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FalbN0I27j7vvmlJQQJj.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FalbN0I27j7vvmlJQQJj.png?auto=format&amp;w=1000 1000w&quot; width=&quot;500&quot; /&gt;
    &lt;figcaption&gt;A color ramp used to look up the visible color from a star&#39;s color index.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The color ramp looked like this, however I needed to access its bitmap color data from JavaScript. The way I did this was to first load the image onto DOM, draw it into a canvas element, then access the canvas bitmap.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// make a blank canvas, sized to the image, in this case gradientImage is a dom image element&lt;/span&gt;&lt;br /&gt;gradientCanvas &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;canvas&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;gradientCanvas&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;width &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; gradientImage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;width&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;gradientCanvas&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;height &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; gradientImage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;height&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// draw the image&lt;/span&gt;&lt;br /&gt;gradientCanvas&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;2d&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;drawImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; gradientImage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; gradientImage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;width&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; gradientImage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;height &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// a function to grab the pixel color based on a normalized percentage value&lt;/span&gt;&lt;br /&gt;gradientCanvas&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;getColor&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;percentage&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;2d&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getImageData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;percentage \&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; gradientImage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;width&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This same method is then used for coloring individual stars in the star model view.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;My eyes!&quot; decoding=&quot;async&quot; height=&quot;236&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 500px) 500px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J9xkA6vu8V6BBbkjQ2og.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J9xkA6vu8V6BBbkjQ2og.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J9xkA6vu8V6BBbkjQ2og.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J9xkA6vu8V6BBbkjQ2og.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J9xkA6vu8V6BBbkjQ2og.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J9xkA6vu8V6BBbkjQ2og.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J9xkA6vu8V6BBbkjQ2og.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J9xkA6vu8V6BBbkjQ2og.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J9xkA6vu8V6BBbkjQ2og.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J9xkA6vu8V6BBbkjQ2og.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J9xkA6vu8V6BBbkjQ2og.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J9xkA6vu8V6BBbkjQ2og.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J9xkA6vu8V6BBbkjQ2og.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J9xkA6vu8V6BBbkjQ2og.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J9xkA6vu8V6BBbkjQ2og.png?auto=format&amp;w=1000 1000w&quot; width=&quot;500&quot; /&gt;
    &lt;figcaption&gt;The same technique is used to do color lookup for a star&#39;s spectral class.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;shader-wrangling&quot;&gt;Shader wrangling &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/100000stars/#shader-wrangling&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Throughout the project I discovered that I needed to write more and more shaders to accomplish all of the visual effects. I wrote a custom shader loader for this purpose because I was tired of having shaders live in index.html.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// list of shaders we&#39;ll load&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; shaderList &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;shaders/starsurface&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;shaders/starhalo&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;shaders/starflare&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;shaders/galacticstars&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/*...etc...*/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// a small util to pre-fetch all shaders and put them in a data structure (replacing the list above)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;loadShaders&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;list&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; callback&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; shaders &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; expectedFiles &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length \&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; loadedFiles &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;makeCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; type&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; shaders&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;shaders&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    shaders&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;//  check if done&lt;/span&gt;&lt;br /&gt;    loadedFiles&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; loadedFiles &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; expectedFiles &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token function&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; shaders &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;lt&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;list&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; vertexShaderFile &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;.vsh&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; fragmentShaderFile &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;.fsh&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;//	find the filename, use it as the identifier&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; splitted &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; shaderName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; splitted&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;splitted&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; vertexShaderFile&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;makeCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;shaderName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;vertex&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; fragmentShaderFile&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;token function&quot;&gt;makeCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;shaderName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;fragment&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The loadShaders() function takes a list of shader file names (expecting .fsh for fragment and .vsh for vertex shaders), attempts to load their data, and then just replaces the list with objects. The end result is in your THREE.js uniforms you could pass shaders to it like so:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; galacticShaderMaterial &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;THREE&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ShaderMaterial&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token literal-property property&quot;&gt;vertexShader&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; shaderList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;galacticstars&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vertex&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token literal-property property&quot;&gt;fragmentShader&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; shaderList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;galacticstars&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fragment&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;_&lt;span class=&quot;token char-set class-name&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token char-set class-name&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token char-set class-name&quot;&gt;.&lt;/span&gt;_&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;I probably could have used require.js although that would have needed some code reassembling just for this purpose. This solution, while much easier, could be improved upon I think, perhaps even as a THREE.js extension. If you have suggestions or ways to do this better, please let me know!&lt;/p&gt;
&lt;h2 id=&quot;css-text-labels-on-top-of-threejs&quot;&gt;CSS Text Labels on top of THREE.js &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/100000stars/#css-text-labels-on-top-of-threejs&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;On our last project, Small Arms Globe, I toyed with making text labels appear on top of a THREE.js scene. The method I was using calculates the absolute model position of where I want the text to appear, then resolves the screen position using THREE.Projector(), and finally uses CSS &amp;quot;top&amp;quot; and &amp;quot;left&amp;quot; to place the CSS elements at the desired position.&lt;/p&gt;
&lt;p&gt;Early iterations on this project used this same technique, however I&#39;ve been itching to try &lt;a href=&quot;http://www.emagix.net/academic/mscs-project/item/camera-sync-with-css3-and-webgl-threejs&quot; rel=&quot;noopener&quot;&gt;this other method described by Luis Cruz.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The basic idea: match the CSS3D&#39;s matrix transform to THREE&#39;s camera and scene, and you can &amp;quot;place&amp;quot; CSS elements in 3D as if it were on top of THREE&#39;s scene. There are limitations to this though, for example you won&#39;t be able to have text go underneath a THREE.js object. This is still much faster than trying to perform layout using &amp;quot;top&amp;quot; and &amp;quot;left&amp;quot; CSS attributes.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Text labels.&quot; decoding=&quot;async&quot; height=&quot;287&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 500px) 500px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9GGQ2CoTcQ5DAkgJ2NdA.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9GGQ2CoTcQ5DAkgJ2NdA.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9GGQ2CoTcQ5DAkgJ2NdA.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9GGQ2CoTcQ5DAkgJ2NdA.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9GGQ2CoTcQ5DAkgJ2NdA.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9GGQ2CoTcQ5DAkgJ2NdA.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9GGQ2CoTcQ5DAkgJ2NdA.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9GGQ2CoTcQ5DAkgJ2NdA.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9GGQ2CoTcQ5DAkgJ2NdA.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9GGQ2CoTcQ5DAkgJ2NdA.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9GGQ2CoTcQ5DAkgJ2NdA.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9GGQ2CoTcQ5DAkgJ2NdA.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9GGQ2CoTcQ5DAkgJ2NdA.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9GGQ2CoTcQ5DAkgJ2NdA.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9GGQ2CoTcQ5DAkgJ2NdA.png?auto=format&amp;w=1000 1000w&quot; width=&quot;500&quot; /&gt;
    &lt;figcaption&gt;Using CSS3D transforms to place text labels on top of WebGL.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;You can find the &lt;a href=&quot;http://www.emagix.net/images/portfolio/academic/mscs-project/code_projects/projects/webgl-three-css3-sync01.html&quot; rel=&quot;noopener&quot;&gt;demo (and code in view source) for this here.&lt;/a&gt; However I did find that the matrix order has since changed for THREE.js. The function I&#39;ve updated:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;_ Fixes the difference between WebGL coordinates to CSS coordinates _&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;toCSSMatrix&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;threeMat4&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; threeMat4&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;f &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;f &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; e &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;f&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;epsilon&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;matrix3d(&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;,&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;)&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Since everything is transformed, the text no longer faces the camera. The solution was to use &lt;a href=&quot;https://github.com/mrdoob/three.js/blob/master/src/extras/core/Gyroscope.js&quot; rel=&quot;noopener&quot;&gt;THREE.Gyroscope()&lt;/a&gt; which forces an Object3D to &amp;quot;lose&amp;quot; its inherited orientation from the scene. This technique is called &amp;quot;billboarding&amp;quot;, and Gyroscope is perfect for doing this.&lt;/p&gt;
&lt;p&gt;What&#39;s really nice is that all of the normal DOM and CSS still played along, like being able mouse-over a 3D text label and have it glow with drop shadows.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Text labels.&quot; decoding=&quot;async&quot; height=&quot;267&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 500px) 500px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HVa9xgeH7Dy4YGoGlrC9.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HVa9xgeH7Dy4YGoGlrC9.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HVa9xgeH7Dy4YGoGlrC9.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HVa9xgeH7Dy4YGoGlrC9.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HVa9xgeH7Dy4YGoGlrC9.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HVa9xgeH7Dy4YGoGlrC9.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HVa9xgeH7Dy4YGoGlrC9.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HVa9xgeH7Dy4YGoGlrC9.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HVa9xgeH7Dy4YGoGlrC9.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HVa9xgeH7Dy4YGoGlrC9.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HVa9xgeH7Dy4YGoGlrC9.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HVa9xgeH7Dy4YGoGlrC9.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HVa9xgeH7Dy4YGoGlrC9.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HVa9xgeH7Dy4YGoGlrC9.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HVa9xgeH7Dy4YGoGlrC9.png?auto=format&amp;w=1000 1000w&quot; width=&quot;500&quot; /&gt;
    &lt;figcaption&gt;Having text labels always face the camera by attaching it to a THREE.Gyroscope().&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;When zooming in I found the scaling of typography was causing issues with positioning. Perhaps is this due to the kerning and padding of the text? Another problem was that the text became pixelated when zoomed into since the DOM renderer treats the rendered text as a textured quad, something to be aware of when using this method. In retrospect, I could have just used gigantic font-sized text, and perhaps this is something for future exploration. In this project I also used the &amp;quot;top/left&amp;quot; CSS placement text labels, described earlier, for really small elements that accompany planets in the solar system.&lt;/p&gt;
&lt;h2 id=&quot;music-playback-and-looping&quot;&gt;Music playback and looping &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/100000stars/#music-playback-and-looping&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The piece of music played during Mass Effect&#39;s &#39;Galactic Map&#39; was by Bioware composers Sam Hulick and Jack Wall, and it had the kind of emotion I wanted the visitor to experience. We wanted some music in our project because we felt it was an important part of the atmosphere, helping create that sense of awe and wonderment we were trying to aim for.&lt;/p&gt;
&lt;p&gt;Our producer Valdean Klump contacted Sam who had a bunch of &amp;quot;cutting floor&amp;quot; music from Mass Effect that he very graciously let us use. The track is titled &amp;quot;In a Strange Land&amp;quot;.&lt;/p&gt;
&lt;p&gt;I used the audio tag for music playback, however even in Chrome the &amp;quot;loop&amp;quot; attribute was unreliable -- sometimes it would just fail to loop. In the end this dual audio tag hack was used to check for end of playback and cycling to the other tag for playing. What was disappointing was that this &lt;em&gt;still&lt;/em&gt; was not perfectly looping all the time, alas I feel like this was the best that I could do.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; musicA &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;bgmusicA&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; musicB &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;bgmusicB&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;musicA&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;ended&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pause&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;playB&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;musicB&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// make it wait 15 seconds before playing again&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; playB&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15000&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;musicB&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;ended&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pause&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;playA&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;musicA&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// otherwise the music will drive you insane&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; playA&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15000&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// okay so there&#39;s a bit of code redundancy, I admit it&lt;/span&gt;&lt;br /&gt;musicA&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;room-for-improvement&quot;&gt;Room for improvement &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/100000stars/#room-for-improvement&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After having worked with THREE.js for a while, I feel like I&#39;ve gotten to the point where my data was mixing too much with my code. For example when defining materials, textures, and geometry instructions in-line, I was essentially &amp;quot;3D modeling with code&amp;quot;. This felt really bad, and is an area where future endeavors with THREE.js could greatly improve upon, for example defining material data in a separate file, preferably viewable and tweakable in some context, and can be brought back into the main project.&lt;/p&gt;
&lt;p&gt;Our colleague Ray McClure also spent some time creating some awesome generative &amp;quot;space noises&amp;quot; which had to be cut due to the web audio API being unstable, crashing Chrome every so often. It&#39;s unfortunate… but it definitely got us thinking more in the sound space for future work. As of this writing I&#39;m informed that the Web Audio API has been patched so it&#39;s possible this is working now, something to look out for in the future.&lt;/p&gt;
&lt;p&gt;Typographical elements paired with WebGL still remains to be a challenge, and I&#39;m not 100% sure what we&#39;re doing here is the correct way. It still feels like a hack. Perhaps future versions of THREE, with its &lt;a href=&quot;https://plus.sandbox.google.com/113862800338869870683/posts/PnJB9fAQb6z&quot; rel=&quot;noopener&quot;&gt;up and coming CSS Renderer&lt;/a&gt;, can be used to better join the two worlds.&lt;/p&gt;
&lt;h2 id=&quot;credits&quot;&gt;Credits &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/100000stars/#credits&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Thanks to Aaron Koblin for letting me go to town with this project. Jono Brandel for the excellent UI design + implementation, type treatment, and tour implementation. Valdean Klump for giving the project a name and all of the copy. Sabah Ahmed for clearing the metric ton of use-rights for the data and image sources. Clem Wright for reaching out to the right people for publication. Doug Fritz for technical excellence. George Brower for teaching me JS and CSS. And of course Mr. Doob for THREE.js.&lt;/p&gt;
&lt;h2 id=&quot;references&quot;&gt;References &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/100000stars/#references&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/mrdoob/three.js/&quot; rel=&quot;noopener&quot;&gt;Three.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.emagix.net/academic/mscs-project/item/camera-sync-with-css3-and-webgl-threejs&quot; rel=&quot;noopener&quot;&gt;Camera Sync with CSS3 and WebGL THREE.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://masseffect.bioware.com/&quot; rel=&quot;noopener&quot;&gt;Mass Effect&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Michael Chang</name>
    </author>
  </entry>
</feed>
