<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Bluecadet - Medium]]></title>
        <description><![CDATA[In-progress explorations from the Bluecadet team - Medium]]></description>
        <link>https://medium.com/bluecadet?source=rss----c90dfb25088e---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Bluecadet - Medium</title>
            <link>https://medium.com/bluecadet?source=rss----c90dfb25088e---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 21:49:38 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/bluecadet" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Streamlining Exhibit Installs with Launchpad]]></title>
            <link>https://medium.com/bluecadet/streamlining-exhibit-installs-with-launchpad-dec04a5356ac?source=rss----c90dfb25088e---4</link>
            <guid isPermaLink="false">https://medium.com/p/dec04a5356ac</guid>
            <category><![CDATA[exhibition]]></category>
            <category><![CDATA[experience-design]]></category>
            <category><![CDATA[creative-tech]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[installation]]></category>
            <dc:creator><![CDATA[Benjamin Bojko]]></dc:creator>
            <pubDate>Thu, 14 Sep 2023 17:39:35 GMT</pubDate>
            <atom:updated>2023-09-14T17:39:35.464Z</atom:updated>
            <content:encoded><![CDATA[<h3>Streamlining Exhibit Installs with Launchpad—An Open Source Tool by Bluecadet</h3><p>As a creative technologist I get a lot of joy out of building visual experiences for physical spaces. Many of Bluecadet’s projects run in highly trafficked locations for years, and if you’ve ever worked on a long-term media installation, you’re probably aware of the challenges involved in making them bulletproof. As exhibits scale in size and complexity, the process of installing can become a delicate but repetitive chore. To streamline and standardize this process, we developed <a href="https://github.com/bluecadet/launchpad">Launchpad</a>: an open-source tool that facilitates the deployment and management of exhibit apps and content.</p><h3>Why Build Our Own Tool?</h3><p>Many of our projects are content-heavy, native apps with long runtimes distributed across many PCs. Our experiences need to be reliable, consistent across exhibits and support many types of frameworks like Unity, Cinder or Web. If things go wrong — especially on a large canvas — they need to fail gracefully and recover quickly. We also often pull content from a wide variety of sources and need to cache that content offline for quick and reliable access.</p><p>Typically we need to tick these boxes during our installs:</p><ul><li>Configure Windows OS for exhibit environments</li><li>Run a watchdog to restart processes on exit</li><li>Prevent escape from our apps into the OS</li><li>Regularly sync remote content locally</li></ul><p>There are plenty of great tools for some of these tasks, like Helios Interactive’s <a href="https://github.com/HeliosInteractive/Phoenix">Phoenix</a>, Stimulant’s <a href="https://github.com/stimulant/ampm">AM/PM</a>, Local Projects’ <a href="https://github.com/local-projects/ofxApp">ofxApp</a>, Windows Task Manager, <a href="https://www.ansible.com/">Ansible</a> and <a href="https://pm2.keymetrics.io/">PM2</a>. But none of them provide all the specific settings for PC bootstrapping, monitoring capabilities, content pipelines or extensibility that we needed.</p><h3>Standardizing our Process</h3><p>Before Launchpad, we had long install checklists for each PC that required manual configuration and testing. However, this was prone to errors and time-consuming to manage. During a project for the Gerald R Ford Presidential Library, we found ourselves spending three days in an AV closet installing dozens of programs one-by-one, manually.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Y3bycT_itRPN3UMH28ckvw.jpeg" /><figcaption>Our office for three days while installing dozens of programs for the Gerald Ford Presidential Library.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lUDmjwGeIjIwnAq25HpCgw.png" /><figcaption>Install punch list for a few dozen PCs at a single site. Each step had to be configured and validated manually.</figcaption></figure><p>For a long time, our watchdog of choice was <a href="https://github.com/HeliosInteractive/Phoenix">Phoenix</a>, which is a robust tool but hard to manage and sequence multiple concurrent apps with. For content, we either wrote custom Python scripts or used rysnc and ssh, which was either very specific and time consuming or limited our choice of backends and infrastructure.</p><pre>echo &quot;Downloading media...&quot; <br>rsync -rlvz --size-only --ipv4 --progress -e &#39;ssh -p 2222&#39; --delete --exclude=&#39;*.csv&#39; $ENV.$SITE@appserver.$ENV.$SITE.drush.in:code/wp-content/uploads/*/*/ ./media<br><br>echo &quot;Downloading data...&quot;<br>rsync -rlvz --size-only --ipv4 --progress -e &#39;ssh -p 2222&#39; --delete --include=&#39;*.csv&#39; --exclude=&#39;*&#39; $ENV.$SITE@appserver.$ENV.$SITE.drush.in:code/wp-content/uploads/*/*/ ./data</pre><h3>Automating at Scale</h3><p>Our next big project was for <a href="https://www.bluecadet.com/work/the-henry-ford-museum-of-american-innovation/">The Henry Ford Museum</a> — coincidentally another Michigan-based <em>Ford </em>institution — with one of the more complex installations running on 20 PCs.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7HFG2Lz3hnC54I9QTK25wQ.png" /><figcaption>Calibrating one of the 20 depth cameras at The Henry Ford Museum</figcaption></figure><p>Tired of spending days poring over punch lists, we knew we needed a better solution. That’s why we set out to build a suite of scripts that would automated every step of the process—an early version of Launchpad.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/720/1*sW8glz5ZxaBNdeNV4u4e1g.gif" /><figcaption>Configuring a fresh Windows PC with Launchpad</figcaption></figure><p>This initial version was built as a plugin for <a href="https://github.com/stimulant/ampm">AM/PM</a>. While it worked wonders for our first project at The Henry Ford, we quickly realized that it lacked the necessary extension options and control over launch sequences and app states. As we geared up for our next challenge with over 20 individual programs at the <a href="https://www.bluecadet.com/work/mit-museum/">MIT Museum</a>, we knew we had to up our game.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*Q979CqIDoF1iwmYlAOPkfQ.gif" /><figcaption>Deploying new builds across 20 PCs at The Henry Ford Museum using an old version of Launchpad</figcaption></figure><h3>Standalone Package</h3><p>The next version of Launchpad was built from the ground up as a standalone process or a Node dependency. It combined the best parts of our previous AM/PM plugin and was designed to be easily extended and modular. We swapped in PM2 as our watchdog of choice, which allowed us to run it as a subprocess to manage multiple apps at the same time, get better state readings and add add features like window management.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*deUeQhpXM-p3r28hAnLgQw.png" /><figcaption>Our final Launchpad structure.</figcaption></figure><p>But that’s not all — we revised our content pipeline to support a range of CMS platforms, improve caching and gracefully rollback content in emergencies. We also added text and image processing capabilities, making it easy to resize, filter and crop images or convert CMS-specific syntax to HTML or any other format. Additionally, Launchpad now has streamlined log management and provides hooks to run commands at various stages of your app lifecycle.</p><h3>Why You Should Use Launchpad</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*pWdbbnHxPeatsM9E-y2zeA.gif" /><figcaption>Syncing content and launching apps with Launchpad</figcaption></figure><p>Launchpad has significantly simplified our process of installing complex interactive programs across museums, institutions and other large venues. It takes the guesswork out of configuration and reduces days of work down to minutes for us. After years of testing it on permanent exhibit installs with across many types of backends and frameworks, it’s become a crucial part of our exhibit operations and we’re excited to open source it.</p><h4>What Launchpad Can Do</h4><ul><li>Run multiple apps in parallel, with control over launch sequence and window order</li><li>Fail gracefully and restart apps when they exit</li><li>Automate PC configuration for exhibit environments, e.g. disable consumer features like edge swipes, notifications, updates and prevent PCs from going to sleep</li><li>Sync content from APIs like Sanity, Strapi, Contentful, Airtable, and generic JSON (Wordpress, Drupal, REST APIs)</li><li>Cache content locally in case of network outage, with graceful rollback error handling</li><li>Support for text transformations like markdown to HTML, image processing, resizing, filtering, and cropping</li></ul><h3>Getting Launchpad</h3><p>Launchpad is available on <a href="https://www.npmjs.com/package/@bluecadet/launchpad">NPM</a> and <a href="https://github.com/bluecadet/launchpad">GitHub</a>:</p><p>npm i @bluecadet/launchpad</p><p>Check out our <a href="https://github.com/bluecadet/launchpad">documentation</a> to get started and learn more.</p><p>We’ve deployed many projects with Launchpad and are actively maintaining it. If you have questions or suggestions, <a href="https://github.com/bluecadet/launchpad/issues/new/choose">submit an issue</a> and we’ll look into it.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=dec04a5356ac" width="1" height="1" alt=""><hr><p><a href="https://medium.com/bluecadet/streamlining-exhibit-installs-with-launchpad-dec04a5356ac">Streamlining Exhibit Installs with Launchpad</a> was originally published in <a href="https://medium.com/bluecadet">Bluecadet</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Pantheon and GitHub Actions]]></title>
            <link>https://medium.com/bluecadet/pantheon-and-github-actions-1b9b1dbd5746?source=rss----c90dfb25088e---4</link>
            <guid isPermaLink="false">https://medium.com/p/1b9b1dbd5746</guid>
            <category><![CDATA[continuous-integration]]></category>
            <category><![CDATA[drupal]]></category>
            <category><![CDATA[pantheon]]></category>
            <dc:creator><![CDATA[Pete Inge]]></dc:creator>
            <pubDate>Wed, 10 Aug 2022 13:47:26 GMT</pubDate>
            <atom:updated>2022-08-10T13:47:25.511Z</atom:updated>
            <content:encoded><![CDATA[<h3>Pantheon and GitHub Actions: and all the Goodies in 2022</h3><figure><img alt="A cartoon red button with the word “Deploy!” below." src="https://cdn-images-1.medium.com/max/1024/1*PPh1Tjt00elbt34EaVbrEA.png" /><figcaption>Building and deploying to Pantheon using GitHub Actions</figcaption></figure><p>At <a href="https://www.bluecadet.com/">Bluecadet</a>, we have been using <a href="https://pantheon.io/docs/guides/build-tools/">Pantheon’s Build tools</a> and Circle CI to deploy projects for a few years now. We have also customized the CircleCi jobs for our own agency needs. It has made the development process more robust by giving us tools to manage different environments as well as run tasks like accessibility checks and visual regressions. But a CI Process also posed a maintenance burden on our clients, some of whom do not have internal DevOps or development staff. With an eye toward making the handoff to our clients easier and more accessible, we started investigating Github Actions as a potential continuous integration alternative.</p><p>First, I want to give a shout out to <a href="https://medium.com/u/2ca820790960">Tyler Fahey</a> and his <a href="https://medium.com/swlh/pantheon-and-github-actions-automated-deployments-via-github-actions-c245aa954797">article</a> from 2020. This got me started and I think there are some significant updates I wanted to address, hence this article.</p><p><strong>Why do we use CI?</strong> We have two main needs for CI. First, running Composer so we do not have to commit module and library dependencies to our project repository. Second, we use Node (and Gulp) to compile all our custom front end CSS and JS, and checking in these assets can cause merge conflicts. After that, depending on the project we run some tests (mainly visual regression — but that is a different article). Using Pantheon’s Build Tools to setup a new site, Composer actions are built into the setup and adding in the Node processing was easy to add into the CircleCi config.</p><p>Another thing that is worth pointing out, as an organization, we like to have our canonical repo live on our GitHub account, to take advantage of Github Issues and ultimately as a backup for the codebase. When we first started using Pantheon, we would actually push our changes to both our GitHub repo and the Pantheon repo. We typically have small teams so this was not a huge burden, but it still required developers to remember to push up to both remotes. Having a CI process that triggered on a push to Github and then pushed to Pantheon on its own made our internal dev/deploy process more consistent.</p><p><strong>In recent years though</strong>, Pantheon has been adding some cool new features that we wanted to learn about and take advantage of.</p><ol><li><a href="https://pantheon.io/docs/guides/integrated-composer">Integrated Composer</a> (basically, able to run composer install on Pantheon platform). Very cool! We now do not need CI for this process. But we still need it for our Node and Gulp processing.</li><li><a href="https://pantheon.io/upstreams">Upstreams</a>, which really is not new, but allows for one-click updates in the Pantheon dashboard. As a developer, one-click updates scare me, but after handing off a project to a client, it is pretty handy.</li><li><a href="https://pantheon.io/autopilot">Autopilot</a>, which seems like a great feature, runs automatic updates, and visual regression! To be honest, I do not know much about this feature, but want to continue to look into it. AGAIN, With an eye toward making the handoff to our clients easier, this seems like a great feature when we hand off a project to our clients.</li></ol><p>With the existing Build Tools setup, we do not have access to those features (as far as I understand it, maybe there are some workarounds?). We also need to update our CI process. We did a lot of customization, and the result is hard to maintain. I had too much fun learning and playing with all the possibilities CI provided. At this point in time, it is allowing us to take a good look at GitHub Actions and so far seems to be a great solution that fits our needs.</p><h4>Step 1: Starting</h4><p>The first step to get started now would be to read <a href="https://medium.com/swlh/pantheon-and-github-actions-automated-deployments-via-github-actions-c245aa954797">Tyler Fahey’s article</a>. He really does a great job of explaining the process in the article, and anything here would just be a copy/paste of that article anyways.</p><h4>Step 2: Adding in Integrated Composer</h4><p>OK, now that you have read that, the next step for me would be how to add in Integrated Composer. Most of this happens on the Pantheon side when we create the project. Through Pantheon’s UI, create a new Drupal 9 site with Integrated Composer. You can do WordPress too, there are just a few more steps. Read the Docs on Integrated Composer. Once you push code, Pantheon will do the rest.</p><p>So here is my Github Workflow (This should look extremely familiar if you read Tyler’s article):</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bdecf5cdc8f6c8b52164d7ab13dd4af2/href">https://medium.com/media/bdecf5cdc8f6c8b52164d7ab13dd4af2/href</a></iframe><h4>Step 2B: Deploy to any branch</h4><p>We can update this slightly so we can push to any branch. Just be careful how you name your branches due to Pantheon’s environment naming conventions.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/15f429cfbdbbe5318b769aa6486b1fae/href">https://medium.com/media/15f429cfbdbbe5318b769aa6486b1fae/href</a></iframe><p>We changed:</p><ul><li>the ‘on’ trigger to build on any branch</li><li>the git push command to push to the correct branch</li></ul><h4>Step 3: Adding in Node and Gulp</h4><p>Next, we want to add in Node and the relevant node commands. Our typical process uses Gulp, so you will see commands related to that, but you should be able to substitute it with whatever you need.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4c458fee896a6cf244ae5ebab0231e26/href">https://medium.com/media/4c458fee896a6cf244ae5ebab0231e26/href</a></iframe><p>Let us walk through what we added. First, I borrowed a lot from Pantheon’s build tools to help me understand what needed to happen and how to do it. They have some nice helpful functions and I imagined I could accomplish everything we needed to without it, but it seemed easier just to add it in here for the moment. Lines 16–23 we are installing Terminus and the build tools plugin. What I ended up really needing was this function to cut the .gitignore file. In our “local” repo, we ignore the production assets that Gulp builds (css, js, etc). But we do not want to ignore those files on Pantheon, because otherwise styles and javascript will be missing. Lines 25–27 are running the command to modify the .gitignore file.</p><p>If you are familiar with Pantheon Build Tools process, you will know that there is always this one “Build Assets” commit that is on Pantheon’s repo which is usually huge and contains everything built from Composer as well as Gulp. We basically are replicating that, but now we only need the production assets from our Gulp build.</p><p>Next (lines 29–46), we are adding in all of our Node stuff. We are going to install Node first. Restore any cache for node packages to help speed up the action. Then, finally run the needed commands to build out our Front End assets.</p><p>Now that we have all these new files, we need to get them to Pantheon. Previously, we just pushed to Pantheon, but now we need to create that “Build Assets” commit and then push. The deploy step is updated to handle this.</p><p>Congrats!! We are now using Pantheon’s upstream, and Integrated Composer, GitHub Actions AND Node!</p><p>Read on if you are interested in another use case we ran into…</p><h4>BONUS CONTENT: Ignoring files only on the Pantheon Repo</h4><p>An issue we ran into (and a number of users from what I am told from Pantheon) is if Composer changes files that are not really supposed to be modified from a Composer change, it causes the process to fail on Pantheon. So no CMS! Not good. Also, this was a hard error to understand, but if you think everything is working but your changes are not updating on your build, check the “View log” for that push in your Pantheon dashboard. At the end there will be some errors.</p><p>Our use case is that we are using a handy little plugin, <a href="https://github.com/joachim-n/composer-manifest">joachim-n/composer-manifest</a>, which creates a small yaml file, composer-manifest.yml, indicating exact versions of each composer package installed. This keeps a nice log in your git history for when a package updates, but you did not explicitly change your composer.json file. You could read the lock file, but that is just insane!</p><p>In order to bypass this problem I basically need exactly the opposite of what the Build Tools does in cutting the .gitignore file. And after a little trial and error, I also realized we need to remove those files from being tracked in the repo! So I created a new ignore file, .gitignore-panth. As a standalone file, this has no effect on anything as far as I can tell. If it messes with git in any way, we can rename it. We can then use this file to define files or directories we want to ignore on Pantheon, just like any other .gitignore file. Here is the deploy step now with the 2 new lines we need.</p><pre>- name: deployer<br>      env:<br>        pantheon_repo: &#39;${{ secrets.PANTHEON_REPO }}&#39;<br>      run: |<br>        BASE_BRANCH=${GITHUB_REF##*/}<br>        git remote add pantheon $pantheon_repo<br>        git config user.mail &quot;<a href="/service/mailto:bc-bot@bluecadet.com">bc-bot@bluecadet.com</a>&quot;<br>        git config user.name &quot;Bluecadet Bot&quot;<br>        git rm `cat .gitignore-panth | sed &#39;s/^\///g&#39;`<br>        cat .gitignore-panth &gt;&gt; .gitignore<br>        git add .<br>        git commit -q -m &#39;Build assets for dev.&#39;<br>        git push --force pantheon HEAD:refs/heads/$BASE_BRANCH</pre><p>First, we delete the files with the git rm command, then we simply add the contents of the .gitignore-panth to the real .gitignore file with the cat command.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1b9b1dbd5746" width="1" height="1" alt=""><hr><p><a href="https://medium.com/bluecadet/pantheon-and-github-actions-1b9b1dbd5746">Pantheon and GitHub Actions</a> was originally published in <a href="https://medium.com/bluecadet">Bluecadet</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[AR Winter Wonderland: Celebrating the Holidays With WebAR]]></title>
            <link>https://medium.com/bluecadet/ar-winter-wonderland-celebrating-the-holidays-with-webar-4bd743be3b8c?source=rss----c90dfb25088e---4</link>
            <guid isPermaLink="false">https://medium.com/p/4bd743be3b8c</guid>
            <category><![CDATA[xr]]></category>
            <category><![CDATA[8th-wall]]></category>
            <category><![CDATA[augmented-reality]]></category>
            <category><![CDATA[webar]]></category>
            <category><![CDATA[ar]]></category>
            <dc:creator><![CDATA[Jason Webb]]></dc:creator>
            <pubDate>Fri, 18 Feb 2022 23:02:04 GMT</pubDate>
            <atom:updated>2022-02-18T23:02:03.987Z</atom:updated>
            <content:encoded><![CDATA[<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F659675116%3Fh%3D497403a7fc%26app_id%3D122963&amp;dntp=1&amp;display_name=Vimeo&amp;url=https%3A%2F%2Fvimeo.com%2F659675116&amp;image=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F1332440244-fce1695e87b5c2dc116ca1c8a3ae78fd6adc7400a18b899cdec937cd87d0987c-d_960&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="1080" height="1080" frameborder="0" scrolling="no"><a href="https://medium.com/media/d519944a82d3ea71c80d80b2c4bd7f1b/href">https://medium.com/media/d519944a82d3ea71c80d80b2c4bd7f1b/href</a></iframe><p>Every year at <a href="https://www.bluecadet.com/">Bluecadet</a> we like to make a festive technology project to celebrate the holidays and ring in the new year, and 2021 was no different! We’re always researching and experimenting with new technologies to stay at the top of our game, and recently AR has been on our minds a lot.</p><p>Due to COVID, museums and brands have been eager to find new ways to engage their audiences using contactless, or even remote, interactions. Many are wondering whether AR is a viable option, and we think it definitely has a lot of creative potential to explore.</p><p>However, at the moment the fastest, most stable, and most robust AR engines (Apple’s ARKit and Android’s ARCore) are only available for native apps, which means creators have to navigate the world of app store publishing and visitors have to install another app, which may only work on newer, expensive phones.</p><p>Fortunately, a few platforms and technologies (8th Wall, Zappar, AR.js, and others) have been popping up recently that are using web technologies to provide at least some of the same features as their native counterparts. Now we can build fun AR experiences that nearly anyone can try out without any special apps or fancy hardware!</p><p><strong>Go ahead and scan the QR code below to see for yourself!</strong></p><figure><img alt="Cartoon snowman peeking out from inside an opened present, with a QR code linking to the live app shown on the side of the present." src="https://cdn-images-1.medium.com/max/1024/0*AvNn3ANGXAtlgcuG.jpg" /><figcaption>Scan the QR code above, or go to the following URL to try the app: <a href="https://bluecadet.8thwall.app/holiday-card-2021">https://bluecadet.8thwall.app/holiday-card-2021</a></figcaption></figure><p>In case the app isn’t working or you don’t have a phone nearby, here is a recording of what you would see:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FroriLFcXg-0%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DroriLFcXg-0&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FroriLFcXg-0%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/e1bdda47505c13882acad184731990a3/href">https://medium.com/media/e1bdda47505c13882acad184731990a3/href</a></iframe><h3>How it works</h3><p>After evaluating a few different platforms and code packages, we ended up landing on <a href="https://www.8thwall.com/">8th Wall</a>. Their Cloud Editor, live app previews, multiple 3D framework integrations, and built-in project hosting and deployment all make it easy to set up, build, and launch small to mid-sized projects like ours in no time!</p><h4>World tracking</h4><p>At the core of our AR experience is 8th Wall’s magical <a href="https://www.8thwall.com/products-web">world tracking</a> functionality, which uses a <a href="https://www.8thwall.com/blog/post/45697581391/building-the-next-generation-of-slam-for-the-browser">custom SLAM engine built in C++</a> and <a href="https://www.8thwall.com/blog/post/41173258896/how-we-engineered-ar-for-the-mobile-browser-with-8th-wall-web">compiled for the web with WebAssembly</a>. A JavaScript package developed by 8th Wall interacts with the engine and exposes an <a href="https://www.8thwall.com/docs/web/#api-overview">API with interfaces</a> for ThreeJS, A-Frame, and other modern 3D web frameworks.</p><p>We used 8th Wall’s Cloud Editor with ThreeJS for this experience, which was pretty easy to get up and running with thanks to 8th Wall’s <a href="https://www.8thwall.com/8thwall">sample projects</a>, which can used as starter templates for new projects.</p><p>8th Wall’s JavaScript package provides a scene object that automatically stays aligned with the floor of the environment around you in real-time, adjusting both its orientation and scale to fit as best it can. For me the “a-ha” moment happened when I realized that the XZ plane of the scene was constantly being realigned to the horizontal surface found by the SLAM engine — as you add meshes to the local coordinate system of the scene, they ALL get transformed as 8th Wall aligns the scene to the environment!</p><h4>Assets</h4><p>The 3D models and animations that you see in the experience were all created by our wonderful Motion Designer <a href="https://www.sijichen.com/">Siji Chen</a> using Blender, then exported to compressed GLB files. On the web, asset size makes a huge difference in UX because the assets have to be downloaded by the browser on page load. Our models were ~7–10MB normally, but only ~2MB compressed!</p><p>In ThreeJS the assets were loaded with <a href="https://threejs.org/docs/#examples/en/loaders/GLTFLoader">GLTFLoader</a> and <a href="https://threejs.org/docs/#examples/en/loaders/DRACOLoader">DracoLoader</a>, then animated by just loading up and playing all the baked-in animations created by Siji using ThreeJS’s <a href="https://threejs.org/docs/#manual/en/introduction/Animation-system">animation system</a>. Having all the animations, materials, and geometry all in a single file really saved us time!</p><p>The lighting and environment map setup was borrowed from Don McCurdy’s <a href="https://gltf-viewer.donmccurdy.com/">glTF Viewer</a> app, whose <a href="https://github.com/donmccurdy/three-gltf-viewer">source code</a> helped in a pinch.</p><figure><img alt="Screenshot of the entire winter scene model loaded into the glTF Viewer web app, with a black toolbar shown at the top." src="https://cdn-images-1.medium.com/max/1024/1*5G0ebM51DXvIwilgan4GsA.png" /></figure><h4>UI</h4><p>The experience also includes some flat 2D UI components (built with standard HTML, CSS, and vanilla JavaScript) that sit on top of the canvas, providing some additional features like:</p><ul><li>A “Help” dialog with usage tips, including tips for screen reader users.</li><li>A “reset” button that allows you to force the world tracking engine to reset, in case the 3D models get “stuck” somewhere you don’t want them.</li><li>A manual control interface allowing you to explore the objects in the scene using crosshairs without having to move your phone.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PptONAqapAobb2iv9G-t6w.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6VMUEcs1XW5l92k7MiQcBw.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*18rJ-Q1nv6X422mUq9oh8Q.jpeg" /><figcaption>From left to right: (left) initial screen showing a modal dialog with introductory text; (middle) the same modal dialog with different text containing numbered accessibility tips; (right) phone camera view of a wooden desk with a virtual present placed on it, with the app’s UI components visible.</figcaption></figure><h3>Accessibility</h3><p>We always want to make sure that the projects we create can be enjoyed equally by the widest possible audience, which means creating content and interfaces that are accessible to people with a wide range of abilities.</p><p>We work hard to incorporate guidance from the <a href="https://www.w3.org/TR/WCAG21/">Web Content Accessibility Guidelines</a> (WCAG 2.1 level AA) into our work, and for normal web apps they are a tremendous resource. For AR experiences, though, there are some challenges that are not so easy to solve, so we have to get creative.</p><p>There is still a lot of research to be done on the topic of AR accessibility, so we recommend looking into communities like <a href="https://xraccess.org/">XR Access</a> and <a href="https://www.meetup.com/a11yvr">A11yVR</a> to see what people are thinking about. The <a href="https://www.w3.org/TR/xaur/">XR Accessibility User Requirements</a> Working Group Note published by the W3C’s <a href="https://www.w3.org/WAI/APA/">APA working group</a> is worth a read too!</p><h4>UI accessibility</h4><p>All of the flat 2D UI components were built using simple, semantic HTML elements and minimal ARIA so that screen reader users can easily find, understand, and operate all the controls on the screen.</p><p>Using &lt;button&gt;s for buttons, text alternatives for icons, and standard patterns for common components (like <a href="https://www.w3.org/TR/wai-aria-practices-1.1/examples/dialog-modal/dialog.html">modal dialogs</a>) are all easy ways to make a <em>huge</em> difference in accessibility!</p><h4>Manual control interface</h4><p>In the bottom left corner of the screen is a small gamepad controller icon button that toggles a set of crosshairs and some directional control buttons.</p><p>As the crosshairs pass over objects in the scene, human-friendly labels are shown identifying what those objects are. These labels also get read out automatically by screen readers so that blind and visually-impaired visitors can explore the scene too.</p><figure><img alt="App screenshot with a virtual present appearing to be placed on wooden desk, with two dotted lines (one horizontal, one vertical) spanning screen edge to edge. The manual control interface is expanded, with four directional arrow buttons and a pointer finger icon next to the triggering button." src="https://cdn-images-1.medium.com/max/1024/1*UmCysNZlbon02TPww9goqg.jpeg" /><figcaption>The manual control interface in action.</figcaption></figure><p>For blind and visually-impaired visitors, it’s important that content that is being displayed visually is also available as text somehow (see <a href="https://www.w3.org/WAI/WCAG21/Understanding/non-text-content.html">WCAG 1.1.1</a>). This is especially tricky in AR because part of what a visitor sees on screen is the actual room around them, which is hard (maybe impossible) to appropriately describe in an automated way.</p><p>For the digital content though, we tried to include information that seemed relevant like general descriptions of the scene, announcements of state changes (like opening the present), and contextual instructions explaining how to interact with the scene.</p><p>Here is a recording of what it’s like to use the manual control interface with TalkBack on an Android phone. It’s not perfect, but it’s a start!</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FFXJNNpbbnBg%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DFXJNNpbbnBg&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FFXJNNpbbnBg%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/ff5a4b6376965759294e4e9f109215e6/href">https://medium.com/media/ff5a4b6376965759294e4e9f109215e6/href</a></iframe><h4>Object labelling</h4><p>In future AR apps, we’re not sure whether a manual control interface will be the way to go, but we are confident that it’ll be important for digital content to be labelled with metadata somehow to convey what it represents for assistive technology users, kind of like <a href="https://moz.com/learn/seo/alt-text">alt text for 2D images</a>.</p><p>In theory we could maintain a separate tree of label data whose structure matches the structure of the models in the glTF file, but keeping them synchronized as designs are worked on would be difficult.</p><p>In our models I noticed that the designer had labelled various “collections” of meshes with human-readable names just to help organize them in Blender, which actually get included in the glTF data. With a little post-processing (like removing the numbers) these could work as labels!</p><p>As a visitor moves the crosshairs around in the manual control interface, a ray is cast from the camera to the scene (through the crosshairs’ intersection point). When an object is found, its parent model (“collection” in Blender) is identified and its human-readable name displayed as a visible text label and announced through an ARIA live region.</p><p>For the most part this works great, until multiple models get grouped together under an object named “_center” at the last minute! Maybe in future projects this can be mitigated by the designer, but even better would be a file format that decouples object names from their nesting structures.</p><figure><img alt="Blender screenshot of the 3D scene with a tree view of the mesh objects visible. The tree view is highlighted with a red border." src="https://cdn-images-1.medium.com/max/1024/1*Hs-9zv43UYzd1HnJ1ykqsg.png" /></figure><h4>Future work</h4><p>Accessibility in AR is still an emerging field, with lots of work ahead to come up with inclusive standards and proven patterns that professionals can follow without having to become researchers themselves.</p><p>This presents a real challenge for institutions that want to be at the cutting edge, delivering inclusive experiences that attract and excite visitors. Many of these institutions want their work to be as inclusive as possible, and are legally obligated to do so, but simply don’t know how when it comes to AR.</p><p>Over time, standards and best practices will inevitably emerge as the industry continues to build and share solutions, and we’re excited to see what that brings! Until then, we’ll continue to do our best to experiment with AR, follow the on-going research, and listen to feedback to help create the future of accessible immersive experiences!</p><figure><img alt="Cartoon illustration of two small, happy snowmen standing on either side of a set of two Christmas present boxes." src="https://cdn-images-1.medium.com/max/1024/1*yHR9Fhqs26giolNMU53fEQ.png" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4bd743be3b8c" width="1" height="1" alt=""><hr><p><a href="https://medium.com/bluecadet/ar-winter-wonderland-celebrating-the-holidays-with-webar-4bd743be3b8c">AR Winter Wonderland: Celebrating the Holidays With WebAR</a> was originally published in <a href="https://medium.com/bluecadet">Bluecadet</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[2021 Prototyping Tool Exploration: Introduction & vvvv]]></title>
            <link>https://medium.com/bluecadet/2021-prototyping-tool-exploration-introduction-vvvv-14bb0af78750?source=rss----c90dfb25088e---4</link>
            <guid isPermaLink="false">https://medium.com/p/14bb0af78750</guid>
            <category><![CDATA[prototyping]]></category>
            <category><![CDATA[tools]]></category>
            <category><![CDATA[vvvv]]></category>
            <category><![CDATA[research-and-development]]></category>
            <category><![CDATA[development]]></category>
            <dc:creator><![CDATA[Kevin Zakszewski]]></dc:creator>
            <pubDate>Fri, 21 May 2021 15:03:32 GMT</pubDate>
            <atom:updated>2021-05-21T15:03:32.370Z</atom:updated>
            <content:encoded><![CDATA[<p>At the end of 2020 we put together a shortlist of tools we want to explore in the upcoming year. The focus for each will be on its potential for prototyping, the tool’s features and capabilities to push the bounds of our normal work, and how easy it is to get started and become comfortable with it. Here’s what made the cut:</p><ul><li><a href="https://vvvv.org/">vvvv</a> (gamma and beta)</li><li><a href="https://wiki.qt.io/Qt_Quick">QT Quick</a></li><li><a href="https://cycling74.com/products/max">Max</a></li><li><a href="https://vuo.org/">Vuo</a></li><li><a href="https://openrndr.org/">OPENRNDR</a></li><li><a href="https://derivative.ca/">TouchDesigner</a></li><li><a href="https://hexler.net/products/kodelife">KodeLife</a></li><li><a href="https://processing.org/">Processing</a></li><li><a href="https://unity.com/">Unity</a></li><li><a href="https://www.unrealengine.com/en-US/">Unreal Engine</a></li><li><a href="https://godotengine.org/">GoDot</a></li></ul><p>This first article in the series will focus on <strong>vvvv</strong>. But before we dive in, let’s look at why we’re exploring these tools and what our goals are for this exploration.</p><h4>Potential for Prototyping</h4><p>Prototyping is an integral part of Bluecadet projects as well as our R&amp;D. When we’re exploring potential concepts in strategy, testing and proving a concept, or investigating technical feasibility, we always try to integrate rapid prototyping. We have a few tools that as a studio we’re quite handy with, but it’s always helpful to keep adding to that toolkit.</p><p>R&amp;D at Bluecadet often involves producing, low-fidelity experiments to evaluate if an idea is worth pursuing. It’s often this interesting experimental work that clients see and are inspired by for their own projects with us. Exploring a wider set of tools here allows us to keep pushing the boundaries of our work and experiments.</p><h4>Pushing the Boundaries of our Work</h4><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F245433406%3Fapp_id%3D122963&amp;dntp=1&amp;display_name=Vimeo&amp;url=https%3A%2F%2Fvimeo.com%2F245433406&amp;image=http%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F670113133_1280&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="1920" height="1080" frameborder="0" scrolling="no"><a href="https://medium.com/media/07c26b55d99cf9ab877c55816db7614d/href">https://medium.com/media/07c26b55d99cf9ab877c55816db7614d/href</a></iframe><p>Bluecadet has created some really exciting digital and physical experiences throughout the years. We’ve expanded from websites to digital touchscreens to large-scale environmental experiences. We always want to continue to push that work to be bigger and better experiences. This has led us into spaces like VR, AR, physical LED installations, and digital experiences involving real world objects. As we explore these new tools, we’ll be looking for the potential doorways that open up as a result of this experimenting.</p><p>With this in mind, let’s take a closer look at vvvv.</p><h3>vvvv</h3><p><strong>vvvv </strong>(often pronounced <strong>four vee </strong>or<strong> vee four</strong>) is a hybrid visual/textual live-programming environment for easy prototyping and development. Coming personally from a traditional development background, and more recently a bit of work with TouchDesigner, vvvv’s hybrid environment felt like a great place to start my explorations.</p><h4>When Should I Use vvvv?</h4><p>If you look at the <a href="https://vimeo.com/371511910">vvvv showcase video</a>, you can see it being used for projects like large-scale projection mapping and light shows, virtual reality and augmented reality, digital art experiences, interactive installations, and physical computing. The possibilities are immense for vvvv. I think the versatility of vvvv and the range of mediums you can work with is one of its biggest strengths.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F371511910%3Fh%3D0f2cff66c1%26app_id%3D122963&amp;dntp=1&amp;display_name=Vimeo&amp;url=https%3A%2F%2Fvimeo.com%2F371511910&amp;image=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F829046809-c67d3e47b71b3f052edb6020539088a91df1e8ce84c996d56b36fcd5b1ab9cc6-d_1280&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="1920" height="1080" frameborder="0" scrolling="no"><a href="https://medium.com/media/3761d53695a82cf4527ce0544725d4b0/href">https://medium.com/media/3761d53695a82cf4527ce0544725d4b0/href</a></iframe><p>vvvv also has a lot of potential as a tool for rapid prototyping. The visual programming component of the vvvv environment can make iterations on projects fairly quickly. Additionally, the ability to create projects without any traditional code might make vvvv a good option for designers who want to be closely involved in the prototyping process.</p><p>vvvv comes with some core libraries (including 2D/3D collision detection, 2D drawing with SVG and PDF export, async File IO, among other core features), 3D rendering (based on <a href="https://stride3d.net/">Stride 3D Engine</a>), VR output, as well as low-level shader based workflow. There is also support for devices like the Kinect, Leap Motion controller, and TheEyeTribe Eyetracker, to name a few. Additionally, outside of the standard vvvv library, there are <a href="https://vvvv.org/documentation/using-addons">add ons</a> called packs. These offer additional functionality for your vvvv projects.</p><figure><img alt="Sample patch in vvvv gamma" src="https://cdn-images-1.medium.com/max/1024/1*rvYBL4HJKSfJs12SuqUiPA.png" /><figcaption>Sample patch in vvvv gamma</figcaption></figure><h4>vvvv Beta</h4><p>Currently, there are two versions of vvvv: <strong>vvvv gamma</strong> and <strong>vvvv beta</strong>. Both are similar but have their own unique strengths and weaknesses.</p><p>vvvv beta is the older of the two versions. While this may seem like a drawback, at the time of writing I have found it to be better documented than vvvv gamma. The sample projects and the online community posting their own vvvv Beta work is much more extensive.</p><h4>vvvv Gamma</h4><p>vvvv Gamma, which was released in April 2020, is newer. In general I found vvvv Gamma a little bit more intuitive than vvvv Beta on the surface. However, the lack of samples and documentation (at least compared to vvvv Beta) did make it a little bit challenging to pick up. As I ran through some older tutorials on vvvv Beta, I was able to find similar features in vvvv Gamma once I figured where they were and how to use them. vvvv Gamma feels like the better choice going forward with the caveat to reference vvvv Beta for a more extensive body of work.</p><h4>Personal Experiences</h4><p>If you’re coming from a more traditional programming background, you may find vvvv’s potential exciting, but a little frustrating to pick up. The learning curve can feel a bit steep at times. In my experience, there were quite a few things that seemed very easy to do right out of the box, while I found myself struggling to do things I would consider simple in a more traditional programming environment. The tutorials that come with vvvv are good to get started, but if you want to achieve the larger potential of vvvv, it’s important to understand some of the fundamental concepts and workings of the tool. I found that a lot of these foundations of vvvv are similar to more traditional programming tools, but structured just differently enough that it’s not totally obvious and takes some time to understand.</p><h4>Findings &amp; Takeaways</h4><p>Generally vvvv feels like a very powerful tool for those who are able to take the time to learn how to use it. During the time I was experimenting with it, I never felt totally comfortable with it as a tool, but its potential feels very in line with a lot of the work Bluecadet does. I think with more experience and learning, vvvv will be a good tool for us, especially for rapid prototyping.</p><h4>vvvv Samples &amp; Resources</h4><p>I wanted to share some samples and resources I came across in my research.</p><p><a href="https://vvvv.org/blog/rgb-hunt-0.1">RGB Hunt</a> - A small room physical interactive where users interact with projected objects on the walls of the space.</p><p><a href="https://vvvv.org/blog/elebook-prototype1">EleBook Prototype</a> - A physical book projection interactive where each page offers a different set of interactions.</p><p><a href="http://www.kimchiandchips.com/workshops/omniversity/2012-November/Omniversity%20-%20VVVV,%20Projection,%20Kinect.pdf">Manchester Digital Laboratory / Omniversity of Manchester Press Tutorial</a> -I found this tutorial for vvvv Beta really helpful.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=14bb0af78750" width="1" height="1" alt=""><hr><p><a href="https://medium.com/bluecadet/2021-prototyping-tool-exploration-introduction-vvvv-14bb0af78750">2021 Prototyping Tool Exploration: Introduction &amp; vvvv</a> was originally published in <a href="https://medium.com/bluecadet">Bluecadet</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Just Enough Multiplayer, A Simple Start for Prototyping Realtime Multi-User Spaces with Socket.io]]></title>
            <link>https://medium.com/bluecadet/just-enough-multiplayer-a-simple-start-for-prototyping-realtime-multi-user-spaces-with-socket-io-1fd260ef3e54?source=rss----c90dfb25088e---4</link>
            <guid isPermaLink="false">https://medium.com/p/1fd260ef3e54</guid>
            <category><![CDATA[react]]></category>
            <category><![CDATA[socketio]]></category>
            <category><![CDATA[threejs]]></category>
            <category><![CDATA[multiplayer]]></category>
            <category><![CDATA[webgl]]></category>
            <dc:creator><![CDATA[Chris Arasin]]></dc:creator>
            <pubDate>Fri, 23 Apr 2021 17:33:26 GMT</pubDate>
            <atom:updated>2021-07-06T12:07:07.056Z</atom:updated>
            <content:encoded><![CDATA[<h3>Just Enough Multiplayer: Prototyping Real-Time Multi-User Spaces with Socket.io</h3><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F542150932%3Fapp_id%3D122963&amp;dntp=1&amp;display_name=Vimeo&amp;url=https%3A%2F%2Fvimeo.com%2F542150932%2F94dca1a905&amp;image=http%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F1122411072_295x166.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="488" height="360" frameborder="0" scrolling="no"><a href="https://medium.com/media/a482d151dafc8e57eeea97e4575cabbc/href">https://medium.com/media/a482d151dafc8e57eeea97e4575cabbc/href</a></iframe><p>In the past year, we’ve had a renewed interest in building collaborative applications and virtual spaces where people can come together. While full-featured multiplayer networked games can be complex, we’ve found a simple socket.io server on <a href="https://glitch.com/">Glitch</a> to be an effective tool for rapid multi-user prototyping.</p><h3>Where multiplayer gets complicated</h3><p>Multiplayer games get complicated because of latency.</p><p>If I send an input to move to the left, my local client immediately renders that movement so the game feels snappy. However, it may take 100 milliseconds for that input to get to the game server, run the game simulation to determine what happens with a given input, and then another 100 milliseconds to send the resulting game state to all players. Now, there’s a 200 millisecond difference between where I think I am and where the other players do.</p><p>Competitive multiplayer games get complicated because they must create the illusion that this 200 millisecond difference does not exist. If you’re interested in how this is done, check out this<strong> </strong><a href="https://www.gabrielgambetta.com/client-server-game-architecture.html">fantastic resource by Gabriel Gambetta</a>.</p><p><em>(Oh yeah, also </em><a href="https://gafferongames.com/post/introduction_to_networked_physics/"><em>networked physics</em></a><em>.)</em></p><h3>What’s not so complicated</h3><p>Luckily, we can do a lot without solving this latency issue. For many applications, whether it’s a simulated world or a collaborative tool, we can simply run our virtual environment on each client, respond to updates from the server, and not worry about compensating for the milliseconds between us.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KEI52Jy_hKNxxV_bJecCkQ.gif" /><figcaption>Bluecadet’s prototype 3D model viewer concept with co-presence from <a href="https://www.bluecadet.com/news/virtual-events-are-experiences-why-arent-they-designed-that-way-2/">Virtual Events Are Experiences. Why Aren’t They Designed That Way?</a></figcaption></figure><h3>A Demo Multiplayer App with Glitch and Socket.io</h3><p>The following demo app will get you started with the concepts involved in adding simple multiplayer presence to an interface. The frontend is built with <a href="https://reactjs.org/">React</a>. We will not be covering basic React concepts, so some level of familiarity is assumed. The server is built with Node.js/Socket.io and is hosted on <a href="https://glitch.com/">Glitch</a>. Glitch has been a fun development server environment for our multiplayer prototypes. It’s instantly deployed and allows for quick collaborative iteration.</p><h4>Get the full code repos here:</h4><ul><li><a href="https://github.com/ChrisArasin/simple-multiplayer-client">Frontend React App on Github</a></li><li><a href="https://glitch.com/~simple-multiplayer-server">Backend Node/Socket.io server on Glitch</a></li></ul><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2F6krBX0qjbRE%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D6krBX0qjbRE&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2F6krBX0qjbRE%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/0f8abe3003db089374ad3ef05178b7ff/href">https://medium.com/media/0f8abe3003db089374ad3ef05178b7ff/href</a></iframe><h3>The Frontend</h3><p><a href="https://github.com/ChrisArasin/simple-multiplayer-client">https://github.com/ChrisArasin/simple-multiplayer-client</a></p><p>Our demo app will render a user as a colored circle and move them on click. We’ll use <a href="https://www.react-spring.io/">react-spring</a> to interpolate between user position updates. While not suitable for every use case, getting fewer state updates and handling more of the movement in a client-side interpolation is a nice simplification and can still communicate presence effectively.</p><h4>The Player</h4><p>The player component renders a dot with a position and color. React-spring’s <a href="https://www.react-spring.io/docs/hooks/use-spring">useSpring hook</a> will interpolate between position updates with some configured settings (which are quite fun to play with).</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ef5040c29378bcb089935af16c3fff8a/href">https://medium.com/media/ef5040c29378bcb089935af16c3fff8a/href</a></iframe><h4>The Game Board</h4><p>Our Game board renders all other players at the latest updated position from the server, renders the current player in their latest input position, and captures user input. We separately render the current player so the user does not feel a delay between their input and the application’s response to a server update.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/14af71c8670deb7bb6e2febb97e0bbd3/href">https://medium.com/media/14af71c8670deb7bb6e2febb97e0bbd3/href</a></iframe><h4>Client-Side Network Class</h4><p>The client-side network’s init<em> </em>method is called on mount of the application. This establishes the socket connection, sets at listener for the stateUpdate events the client will receive from the server, and sends an initialize event to the server which will register the current player with its randomly chosen color.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a43a430e13c8cf050b152e1069a9aa03/href">https://medium.com/media/a43a430e13c8cf050b152e1069a9aa03/href</a></iframe><p>When the user clicks to move, we both locally update the user’s position and send this position update to the server.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5d90501d4015297d7a5779dfde47a16e/href">https://medium.com/media/5d90501d4015297d7a5779dfde47a16e/href</a></iframe><h3>The Node Socket.io Server</h3><p><a href="https://glitch.com/~simple-multiplayer-server">Glitch: The friendly community where everyone builds the web</a></p><p>Our server will accomplish a few tasks:</p><ul><li>Keep track of the current state of all players</li><li>Handle new users joining</li><li>Send updates to each client of the current player state</li><li>Remove a user when they leave.</li></ul><h4>Player State</h4><p>Our state data will be an object holding player information, keyed by their IDs. The createPlayer function will be called when a user joins to build a new player object.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/59afac38c3657683d78159f707a03abb/href">https://medium.com/media/59afac38c3657683d78159f707a03abb/href</a></iframe><h4>Handling Player Events</h4><p>We set up a handler for when the client side application calls io.connect. In this handler, we set up listeners for the three events the client can trigger.</p><p>When the client application sends the initialize event, we create a new player with the corresponding id and color, and add that new player data to the game state. We also begin emitting updates to all players if this is the first player joining (discussed in the next section.)</p><p>When the client sends a positionUpdate event, we update that player’s position in game state.</p><p>When a player leaves, the disconnect event is sent, and we remove that player from state.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/df3ffcc62278878c277236499fdd7001/href">https://medium.com/media/df3ffcc62278878c277236499fdd7001/href</a></iframe><h4>Broadcasting State Updates</h4><p>Once users have joined and we are updating their position in the server-side game state, we must broadcast these updates to all players.</p><p>The core of this function is io.emit(&#39;stateUpdate&#39;, players) call. This sends a snapshot of the current player data to all connected users. On the client side, this state object is used to interpolate each player to their updated position.</p><p>We have a stateChanged flag which prevents an update from being sent if there has not been an event that changed the application state since its last update.</p><p>We then confirm that players are still connected, and schedule another stateUpdate message at a given interval.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d3bc433f7804b7c9da0581e898b57e39/href">https://medium.com/media/d3bc433f7804b7c9da0581e898b57e39/href</a></iframe><h3>Extending the Pattern</h3><p>This pattern can be summarized as:</p><ol><li><em>Client-side:</em> Emit a user action as a message to the server</li><li><em>Server-side:</em> Update shared application state</li><li><em>Server-side:</em> In a game loop, broadcast that shared state to connected users</li><li><em>Client-side:</em> Interpolate between state updates</li></ol><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F494170785%3Fapp_id%3D122963&amp;dntp=1&amp;display_name=Vimeo&amp;url=https%3A%2F%2Fvimeo.com%2F494170785&amp;image=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F1021848315_1280.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="1280" height="720" frameborder="0" scrolling="no"><a href="https://medium.com/media/da286f60d6afcfd3f1c1e1499046d4f2/href">https://medium.com/media/da286f60d6afcfd3f1c1e1499046d4f2/href</a></iframe><p>In our <a href="https://vimeo.com/494170785">Holiday Oasis</a>, we used this flexible pattern to model all user actions, including movement on different surfaces, eating holiday treats, and triggering dance floor fireworks. It should serve as a solid foundation for bringing multiplayer features to a variety of contexts, whether it’s a real-time collaborative application or a simulated 3D environment.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1fd260ef3e54" width="1" height="1" alt=""><hr><p><a href="https://medium.com/bluecadet/just-enough-multiplayer-a-simple-start-for-prototyping-realtime-multi-user-spaces-with-socket-io-1fd260ef3e54">Just Enough Multiplayer, A Simple Start for Prototyping Realtime Multi-User Spaces with Socket.io</a> was originally published in <a href="https://medium.com/bluecadet">Bluecadet</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[3D Displays: Opportunities & Design Principles]]></title>
            <link>https://medium.com/bluecadet/r-d-3d-display-technologies-bebe08649beb?source=rss----c90dfb25088e---4</link>
            <guid isPermaLink="false">https://medium.com/p/bebe08649beb</guid>
            <category><![CDATA[development]]></category>
            <category><![CDATA[3d-display]]></category>
            <category><![CDATA[design]]></category>
            <category><![CDATA[research-and-development]]></category>
            <dc:creator><![CDATA[Adiel Fernandez]]></dc:creator>
            <pubDate>Mon, 12 Apr 2021 13:23:37 GMT</pubDate>
            <atom:updated>2021-04-13T20:02:08.753Z</atom:updated>
            <content:encoded><![CDATA[<p>With some exciting projects in the pipeline here at Bluecadet, we’ve been looking into new ways to display engaging content beyond run-of-the mill pixels and projectors. Having these specific visuals and projects in mind as well as general interest in emerging technologies, we decided to spend some R&amp;D time looking into 3D displays and how to use them successfully.</p><p>As with any display technology, it helps to look carefully at what its capabilities are in order to make sure it’s successful. Our goal was to test three different displays and come up with some best-use-case scenarios and a few design principles for visualizing the most impactful content. The four displays discussed below are Looking Glass Factory’s (LGF) <a href="https://lookingglassfactory.com/product/8k">8K</a> and <a href="https://lookingglassfactory.com/portrait">Portrait</a> offerings, the <a href="https://info.magnetic3d.com/downloads/displays/wildfire/m3d-wfpro504k-l">Magnetic 3D 50&quot; Wildfire Pro</a> and the <a href="https://www.sony.com/electronics/spatial-reality-display/elf-sr1">Sony Spatial Reality Display</a>, referred to as “8K”, “Portrait,” and “SRD” respectively.</p><p>Whenever possible, videos are presented below in full resolution and frame rates so the displays can be fully appreciated and evaluated. Obviously, the full 3D effect can’t be fully appreciated with a single camera lens, but these should give a rough sense of parallax, visual artifacts, etc.</p><p><em>Special thanks to all the vendors listed here. Sony and LGF generously sent us test units (some of which were in early prototyping phase), and Magnetic 3D organized a safe in-person review and walkthrough.</em></p><h3>Tech Overview</h3><p>The displays below accomplish a 3D effect through 2 different methods, each of which have specific design constraints and caveats for use.</p><h4>Light Field Displays</h4><p>The LGF and Magnetic 3D displays make use of a sophisticated lenticular film on the screen surface that channels not only slightly different images into each eye (i.e. a “stereo-pair”), but also a different set of images based on where you are located with respect to the screen.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4McMUaiE5JeoZUKJ08wnNA.png" /><figcaption>Light Field Created by Lenticular Display (Image Credit: Looking Glass Factory)</figcaption></figure><p>In order to drive these screens, content must be sent to them with all possible views of the 3D subject stitched together into a “quilt.” The internal processor of the screen receives the quilt image and arranges the thin slices of pixels to work with the micro lens to achieve the 3D effect. As seen below, LGF displays require a 45 image quilt to achieve a smooth, high-fidelity parallax effect. By contrast, Magnetic 3D displays are capable of running a single 2D image (for use as a regular display), a double image stereo pair, and 9 image quilt.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AwDBPbfOaa5K7UyNBfsc9Q.png" /><figcaption>Sample Image “Quilt” fed to Looking Glass Factory Display (Image Credit: Looking Glass Factory)</figcaption></figure><p>It is important to note that this technology does not allow for vertical parallax, meaning the stereoscopic effect is only created along the horizontal axis, from left to right. And while the 3D effect is certainly perceivable <strong>while standing still, it is much more effective when the user can move and truly appreciate the parallax</strong>.</p><p>Due to the nature of the technology, some subtle visual artifacts will always be present and are important to keep in mind:</p><ul><li><em>Moving between stereo-pairs</em> (i.e. the images of the left and right eye get momentarily switched) a subtle “cross-eyed” effect may be noticeable. This is less apparent on displays that have more images in their quilt as there are more image pairs available with less horizontal distance between them.</li><li>As the <em>maximum viewing angle</em> allowed by the micro lenses is reached, the image will “fail,” giving conflicting visual information.</li><li>Within the 3D scene being visualized, <em>content positioned too close or too far from the viewer may </em>cause image ghosting. This results in a “sweet spot” for content somewhere in the middle that gives the best parallax effect while reducing artifacts.</li></ul><p>The greatest advantage of this technology is that it provides a multi-user, “glasses-free” approach, allowing multiple people to enjoy the content with no additional equipment (i.e. barrier for entry).</p><h4>Eye/Facial Tracking Displays</h4><p>Facial tracking displays like Sony’s SRD take advantage of the micro lenses mentioned above in addition to a facial tracking camera embedded within the display’s housing. This allows the display to track the position of the user in space and show a view of the 3D subject that would correspond to where they are with respect to the screen. This allows for both horizontal <em>and</em> vertical parallax, giving users more freedom to examine the subject from various angles.</p><p>The biggest limitation of this methodology is that it is truly a single user display. Any other viewers in the space aside from the primary, facially tracked user will see a warped perspective designed solely for the first user. A similar problem exists if there are <strong>no users</strong> present: the display doesn’t know what perspective to show. In this case, it is important to leverage the facial tracking data (or lack thereof) and have the screen fall back to a 2D image to attract users as opposed to a visually distorted image. Sony’s SRD SDK does this automatically.</p><p>Naturally, these types of displays will also suffer from the shortcomings of facial tracking technology in general. Masks and other types of facial coverings, whether pandemic or culturally related, will limit tracking ability. Darker skin tones are also a known failure point for many facial tracking toolkits so it is important to thoroughly test lighting conditions, device positions, and other factors to maximize accessibility for all users.</p><h3>Designing For 3D Displays</h3><p>With any of the displays below, one thing is exceedingly clear: they are NOT regular screens. The content intended to be shown MUST be carefully curated and designed to maximize the 3D effect. Below are some principles and suggestions we noted to make 3D visuals more successful.</p><ul><li>Solitary “hero” objects work best as they provide the largest contrast between the foreground object and the empty space they exist in. Seeing a scene that extends across the whole display and gets cut off at the boundary slightly ruins the illusion. Note the difference between the LGF 8K Squid video versus the LGF rabbit scene below.</li><li>Construct the overall scene to give the viewer as many visual hints for depth as possible. Things like lighting, movement, and background selection give the brain additional cues that cause us to perceive depth. A high contrast object on a flat background, artificially scaling an object up as it moves toward the user in 3D space, and backgrounds that provide perspective or a horizon line will amplify perceived depth.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/0*5r2FI_DyrNm9axCo" /><figcaption>Magnetic 3D display taking advantage of the background and lighting to amplify the feeling of depth.</figcaption></figure><ul><li>Tailor the content to the spatial setting and use case. Insetting the display within cabinetry or setting it just beyond reach will prevent users from getting too close and ruining the illusion. Using large hero objects/3D text with dramatic depth transitions call attention from a distance while using subtle parallax effects to augment closer viewing.</li><li>Be careful with text use. Each display’s own SDK provides ways of rendering text for maximum readability but image artifacts can cause text to be difficult to read or even disorienting if not used carefully.</li><li>All the displays reviewed have some type of lenticular coating making them highly susceptible to damage from touching, scratching, and finger oils. Protective glass is necessary in publicly viewable use cases.</li><li>In light field displays, be wary of visualizations requiring lots of small objects like particle systems or point clouds. The small dots partnered with visual artifacts from the lenticular displays can create unflattering moire patterns and visual confusion.</li></ul><h3>Looking Glass Factory Displays</h3><h4>Looking Glass 8K</h4><p>Upon first view, the 8K display is a substantial piece of equipment. It is very solidly constructed with a thick pane of optical glass and a solid aluminum chassis. Make sure you get a spotter when moving it around and have proper support for any mounting scenario! In addition to substantial hardware, there are a substantial number of pixels. 8K worth of pixels is a lot to drive in addition to the overhead of rendering 3D content in 45 different views required by LGF. Make sure your computer is equipped with a capable graphics card with 2 display port connections along with a USB 3.0 connection to control the display. While LGF recommends a Nvidia RTX 2080, we tested the 8K successfully on a GTX 1080 Ti.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F529012586%3Fapp_id%3D122963&amp;dntp=1&amp;display_name=Vimeo&amp;url=https%3A%2F%2Fvimeo.com%2F529012586&amp;image=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F1094783160_1280.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="1920" height="1080" frameborder="0" scrolling="no"><a href="https://medium.com/media/b2bc06faabd591332fa48f2ea2e36947/href">https://medium.com/media/b2bc06faabd591332fa48f2ea2e36947/href</a></iframe><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F529023848%3Fapp_id%3D122963&amp;dntp=1&amp;display_name=Vimeo&amp;url=https%3A%2F%2Fvimeo.com%2F529023848&amp;image=http%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F1094801549_1280.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="1920" height="1080" frameborder="0" scrolling="no"><a href="https://medium.com/media/5419fd05e78a91176c23f858883c5c83/href">https://medium.com/media/5419fd05e78a91176c23f858883c5c83/href</a></iframe><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F529026747%3Fapp_id%3D122963&amp;dntp=1&amp;display_name=Vimeo&amp;url=https%3A%2F%2Fvimeo.com%2F529026747&amp;image=http%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F1094806613_1280.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="1920" height="1080" frameborder="0" scrolling="no"><a href="https://medium.com/media/aef53d11ee6409029604035656d0f28f/href">https://medium.com/media/aef53d11ee6409029604035656d0f28f/href</a></iframe><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F529018302%3Fapp_id%3D122963&amp;dntp=1&amp;display_name=Vimeo&amp;url=https%3A%2F%2Fvimeo.com%2F529018302&amp;image=http%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F1094791870_1280.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="1920" height="1080" frameborder="0" scrolling="no"><a href="https://medium.com/media/ddb25ccc6aa54d0f45bc23ac45fd1d3c/href">https://medium.com/media/ddb25ccc6aa54d0f45bc23ac45fd1d3c/href</a></iframe><h4>Looking Glass Portrait</h4><p>The portrait display is quite small and lightweight, lacking the thick glass of its larger siblings. At the time of review, the portrait was still in development and included pre-baked content which did not require a PC to run, only a USB cable to power the internal Raspberry Pi 4.</p><p><em>(Apologies for the portrait orientation videos for those on desktop!)</em></p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F529042750%3Fapp_id%3D122963&amp;dntp=1&amp;display_name=Vimeo&amp;url=https%3A%2F%2Fvimeo.com%2F529042750&amp;image=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F1094835577_960.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="1080" height="1920" frameborder="0" scrolling="no"><a href="https://medium.com/media/4736b8143a5ccf35c983bbc10a51f2e0/href">https://medium.com/media/4736b8143a5ccf35c983bbc10a51f2e0/href</a></iframe><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F529042872%3Fapp_id%3D122963&amp;dntp=1&amp;display_name=Vimeo&amp;url=https%3A%2F%2Fvimeo.com%2F529042872&amp;image=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F1094834590_960.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="1080" height="1920" frameborder="0" scrolling="no"><a href="https://medium.com/media/dfb0e4a4d06f0b90db8d7fe7c981d4d2/href">https://medium.com/media/dfb0e4a4d06f0b90db8d7fe7c981d4d2/href</a></iframe><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F529042801%3Fapp_id%3D122963&amp;dntp=1&amp;display_name=Vimeo&amp;url=https%3A%2F%2Fvimeo.com%2F529042801&amp;image=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F1094834741_960.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="1080" height="1920" frameborder="0" scrolling="no"><a href="https://medium.com/media/0a860c6f40983afb0385be075cedd6de/href">https://medium.com/media/0a860c6f40983afb0385be075cedd6de/href</a></iframe><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F529042674%3Fapp_id%3D122963&amp;dntp=1&amp;display_name=Vimeo&amp;url=https%3A%2F%2Fvimeo.com%2F529042674&amp;image=http%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F1094834789_960.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="1080" height="1920" frameborder="0" scrolling="no"><a href="https://medium.com/media/95f7395872e434ff8bf2e9c2e139e327/href">https://medium.com/media/95f7395872e434ff8bf2e9c2e139e327/href</a></iframe><h4>Notes</h4><ul><li>Optimum viewing distance was roughly 6-12 feet, but viewing up close was not bad. From far away, many visual artifacts disappear.</li><li>The image on the 8K is surprisingly crisp considering that the <strong>many </strong>pixels are split up by the 45 image quilt (equivalent to a roughly 1145 x 644 pixel image).</li><li>45 discrete views within the quilt makes visual artifacts between stereo-pairs very subtle and is overall a pretty seamless experience while moving around.</li><li>The measured horizontal viewing angle is about 50 degrees total, meaning about 25 degrees off-center until the image “fails.” At the maximum viewing angle the 8K will give a conflicting stereo-pair causing a nonsensical image, while the Portrait contains a privacy filter that causes the image to darken at extreme angles before the “fail” point.</li><li>The thick glass on the 8K display is beautiful and eye catching, however it will produce a bit of glare so the location of the display with respect to ambient lighting should be planned carefully.</li><li>The Portrait lacks the glass and because of this it looks like you’re peering into a portal.</li><li>LGF requires an OS level service called HoloPlay (available for Windows and OSX) and has <a href="https://docs.lookingglassfactory.com/DevTools/">plugins</a> for Unity, Unreal, Maya, Blender, DepthKit and more.</li><li>Visualizing text or other flat imagery needs to be done thoughtfully and positioned carefully within the 3D scene on the convergence plane (see “LGF Text” video below).</li><li>For debugging/development, the 8K screen must be attached as a secondary, extended display but micro lens prevents the desktop from being readable so it is not quite usable as a monitor during debugging/setup. We did not test using the LGF as a stand-alone display.</li><li>LGF supports display tiling relatively easily within the SDK in order to build a video wall. However, all displays have a fairly large bezel that should be factored into the video wall design. For the Portrait display, the privacy filter should be taken into account when tiling as the image may go dark when viewing several side by side.</li></ul><h4>Magnetic 3D Display</h4><p>Magnetic 3D has a wide array of offerings, with screens of all sizes and are available to work out custom solutions from small displays to large 100&quot; screens and tiled video walls. We were able to view the 50&quot; Wildfire Pro display and sample content with a Magnetic 3D representative, but were not able to drive the display with our own content.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F529049597%3Fapp_id%3D122963&amp;dntp=1&amp;display_name=Vimeo&amp;url=https%3A%2F%2Fvimeo.com%2F529049597&amp;image=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2Fdefault_1280&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="1920" height="1080" frameborder="0" scrolling="no"><a href="https://medium.com/media/e1e67bcf15170a763954a8c007e9cdd3/href">https://medium.com/media/e1e67bcf15170a763954a8c007e9cdd3/href</a></iframe><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F529049378%3Fapp_id%3D122963&amp;dntp=1&amp;display_name=Vimeo&amp;url=https%3A%2F%2Fvimeo.com%2F529049378&amp;image=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F1094846975_1280.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="1920" height="1080" frameborder="0" scrolling="no"><a href="https://medium.com/media/1ce633b4f889b454ab18f6fd83346af8/href">https://medium.com/media/1ce633b4f889b454ab18f6fd83346af8/href</a></iframe><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F529049238%3Fapp_id%3D122963&amp;dntp=1&amp;display_name=Vimeo&amp;url=https%3A%2F%2Fvimeo.com%2F529049238&amp;image=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2Fdefault_1280&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="1920" height="1080" frameborder="0" scrolling="no"><a href="https://medium.com/media/7133eed0feb5139f723ea408119df56f/href">https://medium.com/media/7133eed0feb5139f723ea408119df56f/href</a></iframe><h4>Notes</h4><ul><li>The 50&quot; Wildfire is a 4K display that utilizes a 9 tile quilt, meaning each image is roughly 1,280 x 720 pixels.</li><li>Fewer images in quilt mean more discrete “steps” as you move between stereo-pairs from left to right and slightly more noticeable “cross-eyed” artifacts when between stereo-pairs.</li><li>Screen is very flexible and has settings via remote control to switch between a single image, double image, and 9 tile quilt. This is very helpful when switching back and forth between computer desktop and 3D content or when lower fidelity is needed for performance considerations.</li><li>M3D officially supports the display with a Unity SDK, but the display could potentially work with other platforms provided they output the same 9 perspective views.</li><li>Since custom displays are not uncommon for the company, lower bezel displays can be leveraged to make video walls or embed displays into exhibit/installation cabinetry.</li><li>Perceived depth of display <em>feels</em> a bit deeper than LGF displays by a small margin, about 12-18,&quot; with the sweet spot of the visualization being 8-10&quot;.</li></ul><h3>Sony Spatial Reality Display</h3><p>In a different category from the previous displays, the Sony SRD is a facial tracking display and contains a camera embedded on top of a 15.6&quot; 4K display. The screen and camera are set within a prism shaped metal housing and includes two triangular pieces that attach via magnets that help frame the content and enhance the illusion of depth. The display requires a PC with a simple HDMI port and USB connection for the camera, and as expected requires a strong GPU capable of real time 3D rendering of 4K content.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F529045158%3Fapp_id%3D122963&amp;dntp=1&amp;display_name=Vimeo&amp;url=https%3A%2F%2Fvimeo.com%2F529045158&amp;image=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F1094839361_1280.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="1920" height="1080" frameborder="0" scrolling="no"><a href="https://medium.com/media/64a6537d4a7916de9c48bd0a13c0e834/href">https://medium.com/media/64a6537d4a7916de9c48bd0a13c0e834/href</a></iframe><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F529045537%3Fapp_id%3D122963&amp;dntp=1&amp;display_name=Vimeo&amp;url=https%3A%2F%2Fvimeo.com%2F529045537&amp;image=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F1094839646_1280.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="1920" height="1080" frameborder="0" scrolling="no"><a href="https://medium.com/media/ad276cc193b4e9c0bc3e5e6e3ee13646/href">https://medium.com/media/ad276cc193b4e9c0bc3e5e6e3ee13646/href</a></iframe><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F529045421%3Fapp_id%3D122963&amp;dntp=1&amp;display_name=Vimeo&amp;url=https%3A%2F%2Fvimeo.com%2F529045421&amp;image=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F1094839223_1280.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="1920" height="1080" frameborder="0" scrolling="no"><a href="https://medium.com/media/2e15962175f6dc64b0cad5594de762dc/href">https://medium.com/media/2e15962175f6dc64b0cad5594de762dc/href</a></iframe><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F529045262%3Fapp_id%3D122963&amp;dntp=1&amp;display_name=Vimeo&amp;url=https%3A%2F%2Fvimeo.com%2F529045262&amp;image=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F1094839064_1280.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="1920" height="1080" frameborder="0" scrolling="no"><a href="https://medium.com/media/8a3d606599993359d20f4dfdb2d43bfe/href">https://medium.com/media/8a3d606599993359d20f4dfdb2d43bfe/href</a></iframe><h4>Notes</h4><ul><li>The 3D effect is superb, allowing users to feel a very real parallax as they move around the object both horizontally and vertically.</li><li>Unity SDK is available and contains examples for displaying content and getting facial tracking data.</li><li>Facial tracking feels fast and responsive with very little lag or visual artifacts as you move around the display.</li><li>The SRD is set to “fail” gracefully back to a 2D image if no users are found. If multiple users are visible, it will persistently track the first user to step into frame.</li><li>Consistently takes less than 1 second to find face and adjust the perspective when going from obscured to unobscured face.</li><li>Facial tracking is possible on faces wearing masks but not great. It depends on lighting, face angle, etc. The author has medium-to-light brown skin and found the tracking to be on par or better than most open source facial tracking toolkits. However, the testing the display in a dark room definitely deteriorated the tracking quality so be sure to test in different lighting conditions.</li><li>Screen is shiny and glare prone, but the facial tracking is highly dependent on ambient lighting to find users.</li><li>Provides the most “real” hologram experience of all displays seen. Items truly feel touchable.</li><li>Viewing angle of the display is very wide, however facial tracking may fail at very wide viewing angles. It has the widest viewing angle of any 3D display seen.</li><li>Biggest pitfall: It is truly a single user experience. Seeing someone else use the screen feels very strange. If a camera happens to lose its primary user, it may quickly latch on to another user standing far away, which feels very alienating for the first user.</li></ul><h3>Wrap Up</h3><p>All in all, we’re excited to find opportunities to use these displays in the right setting. Together with the right designs, they can be highly eye-catching and impactful without feeling tacky or gimmicky.</p><p>Lastly, I’d like to give a <strong>special thanks </strong><a href="https://lookingglassfactory.com/"><strong>Looking Glass Factory</strong></a><strong>, </strong><a href="https://www.magnetic3d.com/"><strong>Magnetic 3D</strong></a><strong> and </strong><a href="https://www.sony.com/electronics/spatial-reality-display/elf-sr1"><strong>Sony</strong></a> for giving us the opportunity to access and evaluate their displays as they graciously sent us test units, arranged (safe) in-person reviews, and talked us through using their products.</p><p><em>Disclaimer: Bluecadet was not compensated in any way for this review by the aforementioned partners but we do appreciate their openness and the time they spent informing us of their offerings.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bebe08649beb" width="1" height="1" alt=""><hr><p><a href="https://medium.com/bluecadet/r-d-3d-display-technologies-bebe08649beb">3D Displays: Opportunities &amp; Design Principles</a> was originally published in <a href="https://medium.com/bluecadet">Bluecadet</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Drupal Programmatic Translations R&D]]></title>
            <link>https://medium.com/bluecadet/drupal-programmatic-translations-r-d-b59a032752a0?source=rss----c90dfb25088e---4</link>
            <guid isPermaLink="false">https://medium.com/p/b59a032752a0</guid>
            <category><![CDATA[research-and-development]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[drupal]]></category>
            <category><![CDATA[multilingual]]></category>
            <dc:creator><![CDATA[Pete Inge]]></dc:creator>
            <pubDate>Fri, 19 Mar 2021 13:07:20 GMT</pubDate>
            <atom:updated>2021-04-07T19:00:25.854Z</atom:updated>
            <content:encoded><![CDATA[<p>We’ve been having a lot of requests lately for multilingual sites or the ability to upgrade to multilingual in a later phase. (To be honest, I thought by now the web world here in the U.S. would almost be required to build sites in English and Spanish. I’ve started learning Spanish 5 times over the past 10 years anticipating this shift.) Primarily working in Drupal, I’ve played around a lot with the multilingual capabilities and am pleased with it, but have yet to put a multilingual site out into the real world.</p><p>Here at <a href="https://www.bluecadet.com/">Bluecadet</a>, most of our multilingual sites we have developed in WordPress, but when we get into custom integrations, we move into the Drupal realm. I’m constantly programmatically creating and updating nodes with data from external sources and I’ve been curious about how this would work within a multilingual Drupal site. I did not think it would be a big deal, just another programming pattern to learn how to code in Drupal.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gXnO2LDTJPyumdmB_15l1A.png" /><figcaption>An example of a previous WordPress site we built with multilingual support. With offices around the world, the Humanity in Action website supports six different languages.</figcaption></figure><h3>The quick test</h3><p>To begin, I just made my local Drupal 9 test site multilingual, with English and Spanish enabled. There are a lot of helpful articles out there to get you started if you are curious, but it is not crazy. Moving around the admin side of the site though, you have to be careful. I just need to slow down and be purposeful about what I am clicking.</p><p>After it was all set up, I simply played around in <a href="https://www.drupal.org/project/devel_php">Drupal Devel’s PHP</a> editor, using the default “Article” Content Type that is present when you install a site. The process was just a simple step to get the node translation. Editing and updating the node and its translation was as straightforward as expected. Below gets into some of the details.</p><h3>Time to get into the weeds</h3><p>If you regularly work with your nodes in code, this should look familiar.</p><ol><li>Started out creating a node as usual:</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Qp_I_FyqstSNmqii-6KY7g.png" /></figure><p>2. Now I create the Spanish translation and save it:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mAmFn9Sqxe94DxAPbqu8WQ.png" /></figure><p>3. And now to edit the node, we load and edit the English version:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*D6RQebwnOTQAeepNv-_20g.png" /></figure><p>4. And load the Spanish translation and edit:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WcQipHZIM-QziHkg_nQ1kA.png" /></figure><p>Unlike most things with Drupal, this came together pretty quickly with little fuss. I’m sure as I dive in further there will be a lot of nuances to discover, but I can’t wait to work on a multilingual integration for a production site.</p><p>For those interested, here is the full Github gist:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5a2963a3cc1b906e129b44840d06461c/href">https://medium.com/media/5a2963a3cc1b906e129b44840d06461c/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b59a032752a0" width="1" height="1" alt=""><hr><p><a href="https://medium.com/bluecadet/drupal-programmatic-translations-r-d-b59a032752a0">Drupal Programmatic Translations R&amp;D</a> was originally published in <a href="https://medium.com/bluecadet">Bluecadet</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Endless connections: Using AI with digital collections]]></title>
            <link>https://medium.com/bluecadet/connecting-thf-collection-objects-using-ai-c076c09a1352?source=rss----c90dfb25088e---4</link>
            <guid isPermaLink="false">https://medium.com/p/c076c09a1352</guid>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[museums]]></category>
            <category><![CDATA[interactive]]></category>
            <category><![CDATA[exhibition]]></category>
            <dc:creator><![CDATA[SHI Weili]]></dc:creator>
            <pubDate>Wed, 10 Mar 2021 15:38:02 GMT</pubDate>
            <atom:updated>2021-03-15T13:16:16.394Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="User interface of the THF Collection AI Table, using AI to create a “gradient” of visually similar objects" src="https://cdn-images-1.medium.com/max/1024/1*1VlHWwwnXtsr_lpc1SFe0w.jpeg" /><figcaption>User interface of the THF Collection AI Table, using AI to create a “gradient” of visually similar objects</figcaption></figure><p>When we created the <a href="https://www.bluecadet.com/work/thf-connections-table/"><strong>Connections AI Table</strong></a> with The Henry Ford Museum of American Innovation (THF for short), our goal was to demonstrate the interconnectedness of the museum’s collection. THF curators did the heavy work of identifying meaningful connections between many of the objects; we wanted to extend that experience to as much of the collection as possible. To fully provide <strong>endless</strong> connections to our users, we leveraged the computational power of artificial intelligence (AI). Through our research and engineering we found that while the current-stage AI is no substitute for human curators in systematic thinking, it can be adopted as an effective tool to suggest visual connections between seemingly unrelated objects. The result was AI-curated connections that offer never-ending interactive exploration that is both insightful and full of surprises.</p><p>In this post, we share our early work that formed the foundation of the AI aspects of the project. We started prototyping with a full export of THF’s digital collection—containing tens of thousands of object images and related information—and came up with two approaches to create visually meaningful paths between images in the dataset: <strong>visual similarity paths</strong> and <strong>dominant color paths</strong>.</p><h3>Visual Similarity Paths</h3><p>To start, we used a pre-trained deep learning model (<a href="https://keras.io/api/applications/vgg/">VGG16</a>) to analyze every image to get the model’s internal representation of this image (technically called <strong>latent representation</strong>, in the form of an array of numbers). With this information we could find the location of every object in the <strong>“THF universe” </strong>(technically, <strong>latent space</strong>), as indicated in the following graph:</p><figure><img alt="Tens of thousands of THF collection objects plotted based on visual similarity. The original visual information was 4096-dimensional and was reduced to 2D for plotting using the t-SNE algorithm." src="https://cdn-images-1.medium.com/max/1024/1*6jo7wr3GPgI6dgGhty39ug.jpeg" /><figcaption>Tens of thousands of THF collection objects plotted based on visual similarity. The original visual information was 4096-dimensional and was reduced to 2D for plotting using the t-SNE algorithm.</figcaption></figure><p>For any two objects in this universe, we can create a visual similarity path by simply selecting a series of objects between them. <strong>But more specifically, how do we select these objects?</strong></p><h4>Naïve Approach: Linear Interpolation</h4><p>Our first approach was simple. In the latent space, draw a straight line between the two objects and evenly divide the line into a few steps. For each step select an image that is closest to the dividing point from the latent space. This is almost like taking the 2D visualization above, tracing a line through objects, and presenting those back as a path of visually similar objects—but we’ll do this in a higher-dimensional space.</p><figure><img alt="Diagram explaining the linear interpolation approach" src="https://cdn-images-1.medium.com/max/417/0*2KHlpyuQ98x4p7rg" /></figure><p>The original 4096-dimensional latent space is too sparse for the number of images we have: If we simply draw a line between two images in that space, most parts of the line will be in empty space, so the closest images to each step could be drawn from pretty far away. Even if we reduce the latent space to 10 dimensions using the <strong>PCA (primary component analysis) algorithm</strong>, this situation still happens and is reflected by duplicated images in the following example path.</p><figure><img alt="A visual similarity path with duplicate steps in the beginning and end" src="https://cdn-images-1.medium.com/max/933/0*ghMDLmlXMay80loi" /><figcaption>A visual similarity path with duplicate steps in the beginning and end</figcaption></figure><p>After reducing the dimension of the latent space to 3, we mostly get non-repeating paths, as the following example shows.</p><figure><img alt="A path without duplication, but the visual connection between many of the steps is unclear" src="https://cdn-images-1.medium.com/max/933/0*cB-ARlqV63QFql53" /><figcaption>A path without duplication, but the visual connection between many of the steps is unclear</figcaption></figure><p>We also have control of the number of steps we sample along the path. The more steps a path has the smoother the transition is supposed to be. However, the paths formed by linear interpolation only make sense to some degree. After all, when drastically reducing the dimension, we lost a lot of information from the latent representation. Our next approach, shortest path, is able to find more convincing visual connections because it can take advantage of the information in the original high-dimensional data.</p><h4>Sophisticated Approach: Shortest Path</h4><p>First, build an interconnected network (technically a <strong>graph</strong>) of object images using the latent representation data. For each image, find the closest <em>k</em> images in the latent space, and set up a direct connection between them. For all other image pairs, the only possible way to connect them is through other images. Then, for two randomly selected images, run the <a href="https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm"><strong>shortest path algorithm</strong></a> to find a path between them.</p><figure><img alt="Diagram explaining the shortest path approach" src="https://cdn-images-1.medium.com/max/417/0*wZ7w6CLXrG_K7oek" /></figure><p>With this approach we also have a few parameters under control. Similar to linear interpolation, we can reduce the dimension of the latent space. But with shortest path, the higher dimension we keep the more information we make use of, and therefore the better the result. The benefit of dimension reduction is time saving when building the graph. With digital collections there is hardly ever a case where we would need to update this “live” for the user, so we were able to run our algorithm overnight as a daily routine. It has all the time it needs.</p><p>Different from linear interpolation, we can’t explicitly choose how many steps we want the path to be. Instead, we choose how many direct connections to assign to each image when building the graph. The more direct connections the denser the graph is and the shorter an average path will be, and vice versa. However, we don’t want to assign too few direct connections to each image: that might result in image pairs for which a path cannot be found, since the graph would not be fully connected.</p><h4>Example Paths</h4><p>In the original 4096-dimensional latent space:</p><figure><img alt="Example path" src="https://cdn-images-1.medium.com/max/933/0*Z9QZ9p_u65QlhE-J" /></figure><figure><img alt="Example path" src="https://cdn-images-1.medium.com/max/933/0*9LtutfXl_A_v3e_L" /></figure><figure><img alt="Example path" src="https://cdn-images-1.medium.com/max/933/0*3v7yM3ocQ3siAKqJ" /></figure><figure><img alt="Example path" src="https://cdn-images-1.medium.com/max/933/0*8DIxh30MbKKnJsYz" /></figure><figure><img alt="Example path" src="https://cdn-images-1.medium.com/max/933/0*MaZTc3VJGibM1qlL" /></figure><p>We can also slightly reduce the dimensionality of the data for some moderate speed gain. In the reduced 300-dimensional latent space, the paths are nearly as good with occasionally more confusing connections. We ended up not doing this in production, as the speed gain wasn’t worth the weirdness of some of the connections.</p><figure><img alt="Example path" src="https://cdn-images-1.medium.com/max/933/0*fXWDgu12hM8Uy7JO" /></figure><figure><img alt="Example path" src="https://cdn-images-1.medium.com/max/933/0*32UoUY8FFlZwEVuc" /></figure><figure><img alt="Example path" src="https://cdn-images-1.medium.com/max/933/0*i27IdEc9mqZqfOgm" /></figure><figure><img alt="Example path" src="https://cdn-images-1.medium.com/max/933/0*oRg7fsGMH_prO07O" /></figure><figure><img alt="Example path" src="https://cdn-images-1.medium.com/max/933/0*cPbZXACcNZ4xNb0v" /></figure><p>Note that although we can’t explicitly ask for a fixed-step path, we can always extend a path by forming a new one starting from the endpoint of the existing one. The following examples (with each new path starting from the end of the last path) are again from the 4096-dimensional latent space:</p><figure><img alt="Multiple paths with the end of each path identical to the beginning of the next path, forming an endless path" src="https://cdn-images-1.medium.com/max/1024/1*yw8xhqnthjVYLYSQ0TGr5Q.png" /><figcaption>Multiple paths with the end of each path identical to the beginning of the next path, forming an endless path</figcaption></figure><p>Or we can form multiple paths from the same beginning object:</p><figure><img alt="Multiple paths starting from the same object" src="https://cdn-images-1.medium.com/max/1024/1*Ki0S7UUGCw38830bPQH8Gw.png" /><figcaption>Multiple paths starting from the same object</figcaption></figure><p>We can play with longer—and sometimes weirder—connections by selecting a random “middle” point and building a compound path from A to Z to B (Z = the random middle):</p><figure><img alt="Multiple paths starting from the same object, deviating towards different middle points, and converging to the same end. The middle-point object of each path appears twice, because it functions as both the end of the first half and the beginning of the second half of each path." src="https://cdn-images-1.medium.com/max/1024/1*OiSV6jdoKPjF7jVRSVU5kQ.png" /><figcaption>Multiple paths with the same beginning and same end. The middle-point object of each path appears twice because it functions as both the end of the first half and the beginning of the second half of each path.</figcaption></figure><h3>Dominant Color Paths</h3><p>Besides connecting objects with visual similarity, what’s also conceptually interesting (and visually appealing) is connecting them with objects of gradually changing colors, forming a gradient.</p><p>To start, we used the <a href="https://en.wikipedia.org/wiki/K-means_clustering"><strong>k-Means algorithm</strong></a> to get the dominant color of every image. A color is represented by a 3-dimensional vector. Therefore, we can run the same linear interpolation and shortest path approach described above with the color data. The catch is that not all images are vivid enough to pick up their main colors—a few pale images in the path and the color transition won’t make much sense to the eye. After some experimentation, we figured out the following steps to ensure beautiful color paths:</p><p>First, we cropped each image to its central 50% part to reduce the influence of background colors—especially since THF’s collection objects are not uniformly documented against the same background. Then, we ran the k-Means algorithm<strong> </strong>with <em>k</em>=3 (to cluster all pixel colors into 3 clusters and select the centroid color of each cluster). We sorted the selected colors of an object in descending order according to <strong>chroma</strong> (chroma is computed by first converting the RGB color into LAB color space and calculating sqrt(A² + B²)). Essentially chroma gives us a mathematical representation of the image’s vibrancy or color intensity. We then selected the first color that has a chroma no smaller than 2000 (colorful enough) and has a cluster size bigger than 25% of the whole cropped image as the <strong>dominant color</strong> of the image. If none of the colors meet the criteria, we discarded the image because it will be confusing to the eye in a color path.</p><figure><img alt="An example image and its k-Means color list, with the first color selected as the dominant color" src="https://cdn-images-1.medium.com/max/923/0*N0R-9Nn2ksLj5PVq" /><figcaption>An example image and its k-Means color list, with the first color selected as the dominant color</figcaption></figure><p>We use the same linear interpolation and shortest path approach to find paths in the color data. Because the color space is 3-dimensional (low-dimensional) by default, the images seem to be distributed rather evenly. Therefore, both the “naive” linear interpolation approach and the “sophisticated” shortest path approach work pretty well. Actually, linear interpolation might work better because the color path feels more “linear” whereas the shortest path can “zig-zag” in the color space.</p><h4>Example Paths</h4><p>Below are paths among object images, formed by <strong>linear interpolation</strong>. Each image has its dominant color below it. Note that when randomly selecting the beginning and ending images we made sure they are distant enough in the color space to allow interesting transitions between them.</p><figure><img alt="Example path" src="https://cdn-images-1.medium.com/max/933/0*vhndKeBIM8F3VIz6" /></figure><figure><img alt="Example path" src="https://cdn-images-1.medium.com/max/933/0*eICKt_hTveWFPrYu" /></figure><figure><img alt="Example path" src="https://cdn-images-1.medium.com/max/933/0*At-XqnstKWXedvm0" /></figure><p>Paths among object images, formed by <strong>shortest path algorithm</strong>:</p><figure><img alt="Example path" src="https://cdn-images-1.medium.com/max/933/0*JUq6SznX91n5RpXS" /></figure><figure><img alt="Example path" src="https://cdn-images-1.medium.com/max/933/0*SaLZs9njdLxLkl3Q" /></figure><figure><img alt="Example path" src="https://cdn-images-1.medium.com/max/933/0*Zz6ksQmHN0eCd8-e" /></figure><h3><strong>Where to from here?</strong></h3><p>The learnings from this phase went directly into the design and development of the Connections AI Table, with integrated AI-generated connections becoming a key component of the experience. From here, we dove into production:</p><ul><li>Designed a playful, explorative, and meaningful user experience around the technology</li><li>Optimized the performance for the interactive experience</li><li>Integrated the Python/Tensorflow-based AI backend with the C++/Cinder-based interactive frontend</li><li>Streamlined the data processing routine to allow continuous updating of the museum collection data</li></ul><p>What is fascinating in looking back on this work is that in a way we had to become curators ourselves. As you can see above, we had to hone the algorithm and its parameters to find connections that were meaningful for our users. All of the examples are technically “correct” to the AI—but it took actual human input and critical thinking to build something that we felt made sense.</p><p>To learn more about the full creative process for this project, read <a href="https://www.bluecadet.com/news/prototyping-playful-ai/">Playful Prototyping with Machine Learning</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c076c09a1352" width="1" height="1" alt=""><hr><p><a href="https://medium.com/bluecadet/connecting-thf-collection-objects-using-ai-c076c09a1352">Endless connections: Using AI with digital collections</a> was originally published in <a href="https://medium.com/bluecadet">Bluecadet</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Hello LIDAR World]]></title>
            <link>https://medium.com/bluecadet/hello-lidar-world-7ac77fe2470d?source=rss----c90dfb25088e---4</link>
            <guid isPermaLink="false">https://medium.com/p/7ac77fe2470d</guid>
            <category><![CDATA[lidar]]></category>
            <category><![CDATA[iphone-12-pro]]></category>
            <category><![CDATA[threejs]]></category>
            <category><![CDATA[unity]]></category>
            <category><![CDATA[augmented-reality]]></category>
            <dc:creator><![CDATA[Brett Renfer]]></dc:creator>
            <pubDate>Fri, 05 Feb 2021 14:18:28 GMT</pubDate>
            <atom:updated>2021-02-23T19:07:24.453Z</atom:updated>
            <content:encoded><![CDATA[<h3>LiDAR in WebGL: Hello, World</h3><p>We’ve seen a lot of awesome stuff with the new(ish) iPhone 12 Pro’s LiDAR scanner, like this great post at <a href="https://sketchfab.com/blogs/community/3d-scanning-with-the-iphone-12-pro-lidar/">Sketchfab</a>. Naturally, we had to give the process a try ourselves — with an eye towards using this data in creative tech applications.</p><p>TLDR: use an off-the-shelf app to get started faster, and familiarize yourself with <a href="https://www.meshlab.net/">MeshLab</a> if you’d like to render these very detailed captures in code.</p><figure><img alt="WebGL + LIDAR scan + noise, noise, noise" src="https://cdn-images-1.medium.com/max/640/1*g6TTsELGgAXOj52tJNCnPw.gif" /><figcaption>WebGL + LiDAR scan + noise, noise, noise</figcaption></figure><h3><strong>Dipping Toes: Sketching in Unity</strong></h3><p>We do a lot of our AR prototyping in Unity, so that’s where I started. It gives you a lot of features for free, including making meshes of real-world objects and allowing those meshes to occlude objects.</p><figure><img alt="First prototypes with LIDAR meshing in Unity" src="https://cdn-images-1.medium.com/max/320/1*M20v_8_29gqiNFn2wi6IXg.gif" /><figcaption>First prototypes with LiDAR meshing in Unity</figcaption></figure><p>There’s more to be shared there… stay tuned.</p><p>A big downside of Unity’s implementation is that you <strong>don’t get access to the raw LiDAR data</strong>, which includes points, distances, and colors. To play more with capturing the real world, you need to either a) roll your own native point cloud code or b) use an off-the-shelf app.</p><figure><img alt="Sitescape’s pointcloud-based capture" src="https://cdn-images-1.medium.com/max/444/1*mo0P8Rr8v6APPEQbwmQZig.gif" /><figcaption>Sitescape’s pointcloud-based capture</figcaption></figure><p>I chose the latter, following Sketchfab’s lead and downloading <a href="https://apps.apple.com/us/app/3d-scanner-app/id1419913995">3D Scanner App</a> and <a href="https://apps.apple.com/us/app/sitescape-lidar-3d-scanner/id1524700432">SiteScape</a>.</p><h3><strong>Off-the-Shelf Apps: Capturing &amp; Cleanup</strong></h3><p>These apps are great, mostly! Most importantly for me, SiteScape natively exports point clouds PLY files, which are supported by both modeling software as well as <a href="https://threejs.org/examples/webgl_loader_ply.html">THREE.JS</a>, our WebGL framework of choice. 3D Scanner is more interested in making 3D meshes; cool, but not my ideal case here.</p><p>Anyone who has experimented with depth camera data knows: there’s pretty much always <em>some</em> processing you need to do before showing off your shiny model in code. SiteScape is mostly meant for architectural use so it a) captures a ton of points and b) keeps the origin of the model based on where you started scanning. I needed to edit the <strong>density</strong> and <strong>position</strong> of my point cloud to make it workable on the web.</p><p>Our go-to quick modeler/exporter/cleaner-up-er is usually <a href="https://www.blender.org/">Blender</a>. It remains great, but sadly does not support PLY point clouds. It can view ‘em, and <a href="https://blender.stackexchange.com/questions/150072/cant-export-point-clouds-as-a-ply-file">cannot export them</a>. On to another tool!</p><p><a href="https://www.meshlab.net/">MeshLab</a> is another open-source tool, focused specifically on simplifying 3D scans for other uses (e.g. 3D printing).</p><figure><img alt="Using MeshLab to simplify my mesh that has almost 2 million vertices!" src="https://cdn-images-1.medium.com/max/1024/1*33svBwgI2UVM9QllPMm0hg.png" /><figcaption>Perhaps you’ll notice my mesh has almost 2 million vertices…!</figcaption></figure><p>I followed three steps here:</p><ol><li><strong>Reset origin</strong></li></ol><ul><li>Using the <strong>Transform</strong> tool found in <strong>Filters → Normals, Curvature and Orientation</strong>, I reset the origin to “Center on Scene Box”</li></ul><figure><img alt="Using the Transform tool (found in Filters, Normals, Curvature and Orientation) I reset the origin to “Center on Scene Box”" src="https://cdn-images-1.medium.com/max/1024/1*Y0qEEY-aaX7bSMlpvxNVdg.png" /></figure><ul><li>I checked my work by turning on the axes via the lil’ button at the top of the screen. Do that, so you don’t use your mind.</li></ul><figure><img alt="Centering the axes" src="https://cdn-images-1.medium.com/max/1024/1*4G2xYuj9rXupBJWtRpbY-Q.png" /><figcaption>Sweet, those axes are centered.</figcaption></figure><p>2. <strong>Translate</strong></p><ul><li>Using that same tool, I moved my y-axis down to place the origin — and our future user—on the ground</li><li>Make sure you select “Preview” before you apply!</li></ul><p>3. <strong>Reduce</strong></p><ul><li>I used the catchy “Simplification: Clustering Decimation<strong> </strong>” tool under <strong>Filters → Remeshing, Simplification, and Reconstruction</strong> to simplify my mesh</li></ul><figure><img alt="Simplyfing settings" src="https://cdn-images-1.medium.com/max/854/1*3wvaMkLY6_FB5_entg5nQA.png" /><figcaption>My settings. Copy if you dare.</figcaption></figure><ul><li>I played with a few percentage-based settings here — settling on reducing by ~2%.</li><li>Note: many guides will recommend using Quadric Edge Collapse Decimation to simplify. That is slightly easier, but we <em>have no faces since we’re a point cloud</em>. ;(</li></ul><figure><img alt="The mesh with 1.7m vertices" src="https://cdn-images-1.medium.com/max/1024/1*UgXJAZh401ITGwg0fMDAWQ.png" /><figcaption>Before… 1.7m vertices</figcaption></figure><figure><img alt="Mesh with 180k vertices" src="https://cdn-images-1.medium.com/max/1024/1*WAquZEqtWddGLpx0b6yTMQ.png" /><figcaption>After, 180k vertices! It looks very sparse, but I assure you it still works</figcaption></figure><h3>Bringing it into Code: PointClouds in Three.js</h3><p>Full disclosure: this mesh could and should be simplified further! But I was keen to try it out in its (near) full glory.</p><h4><strong>Loading the mesh</strong></h4><p>Loading and displaying a PLY file in Three is fairly simple, once you’ve setup your file and added the PLYLoader external file.</p><pre>var mesh, material, geo; // these become important later</pre><pre>// setup your loader + load your PLY<br>var loader = new THREE.PLYLoader();</pre><pre>loader.load( &#39;../3d/arches.ply&#39;, function ( geometry ) {<br>   geo = geometry;</pre><pre>   material = new THREE.PointsMaterial( { <br>      vertexColors: THREE.VertexColors, <br>      size: 0.05, <br>      sizeAttenuation: true <br>   });</pre><pre>   mesh = new THREE.Points( geo, material );</pre><pre>   // my meshes were still oriented wrong for THREE<br>   // and needed to be scaled; you may not need these</pre><pre>   mesh.rotation.z = - Math.PI / 2;<br>   mesh.scale.multiplyScalar( .5 );</pre><pre>   scene.add( mesh );</pre><pre>});</pre><p>The options in the material proved crucial</p><ul><li><strong>vertexColors</strong> : load color data from your vertices</li><li><strong>size:</strong> rendered size of points; I played with this a lot for effect… it’s very small bc my mesh is in meters</li><li><strong>sizeAttenuation:</strong> change point size based on distance from camera</li></ul><h4><strong>Making it Move with Some Noise</strong></h4><p>I used the very simple <a href="https://github.com/jwagner/simplex-noise.js">SimplexNoise.js</a> to create a “chaos” state for my points I could tween to.</p><ol><li>Setup Points: this code lives in my “loaded” function</li></ol><pre>... </pre><pre>// cached original points<br>pointsGood = geo.attributes.position.array;</pre><pre>// noise points: array based on # of points in mesh<br>pointsNoise = new Float32Array(pointsGood.length);</pre><pre>// points rendered at any given frame<br>pointsLive = new Float32Array(pointsGood.length);</pre><pre>var index = 0;</pre><pre>for (var i=0; i&lt;pointsLive.length; i++){<br>   var x = pointsGood[index];<br>   var y = pointsGood[index+1];<br>   var z = pointsGood[index+2];</pre><pre>   // my use of x,y,z here is arbitrary!<br>   pointsNoise[index++] = this.simplex.noise3D(x,y,z);  //x<br>   pointsNoise[index++] = this.simplex.noise3D(z,x,y);; //y<br>   pointsNoise[index++] = this.simplex.noise3D(y,z,x);; //z<br>}</pre><p>2. Tween between the two, once we’re loaded. In your animate() function…</p><pre>// keep a running increment from 0 - 1<br>// my &quot;pos&quot; var started at 1 in setup()<br>pos = pos * .9 + .1;</pre><pre>// loop through and set &quot;live&quot; points<br>var index = 0;</pre><pre>for (var i=0; i&lt;pointsLive.length; i++){<br>   var x = pointsGood[index];<br>   var y = pointsGood[index+1];<br>   var z = pointsGood[index+2];</pre><pre>   // lerp between &quot;good&quot; and &quot;noise&quot; points</pre><pre>   pointsLive[index++] = <br>      THREE.Math.lerp( pointsNoise[index], x, pos ); <br>   pointsLive[index++] = <br>      THREE.Math.lerp( pointsNoise[index], y, pos );<br>   pointsLive[index++] = <br>      THREE.Math.lerp( pointsNoise[index], z, pos ); //z<br>}</pre><pre>// tell Three your points are updated<br>geo.addAttribute(&quot;position&quot;, <br>   new THREE.BufferAttribute( pointsLive, 3 ) );</pre><pre>geo.attributes.position.needsUpdate = true;</pre><p>And voila! A fast-ish demo of a big LiDAR mesh in Three.JS.</p><p>Check it out <a href="https://distracted-boyd-e5ff56.netlify.app/">here</a>. (Yes, it takes a long time to load, still!), and if you’d like, look at my sketchy code <a href="https://github.com/robotconscience/ThreePointCloud">here</a>.</p><h3><strong>What’s Next?</strong></h3><p>Now that we have our pipeline down, there’s a lot left to try:</p><ul><li><strong>New types of scans</strong>: I’d love to scan some smaller objects, and play with simplification settings to find the balance between “good” and “portable”.</li><li><strong>More efficient code</strong>: The above is a prototype, so it’s lazy. Moving manipulation of the points into a shader would make it infinitely faster, and open up possibilities of tweeting between different scans, having the points drift every frame, etc.</li><li><strong>Rolling our own scanner?</strong> SiteScape is awesome because it’s free and super full-featured. But, it smartly culls a lot of data for you, which led to some underwhelming scans when I tried to capture statues in Grand Army Plaza. Since we might be more focused on museum-style use cases, we’ll eventually need to try out our own, special toolset.</li></ul><p>Until next time, ✌️</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7ac77fe2470d" width="1" height="1" alt=""><hr><p><a href="https://medium.com/bluecadet/hello-lidar-world-7ac77fe2470d">Hello LIDAR World</a> was originally published in <a href="https://medium.com/bluecadet">Bluecadet</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Google Drive, Node.js, and a custom Electron app]]></title>
            <link>https://medium.com/bluecadet/google-drive-node-js-and-a-custom-electron-app-179191d612b9?source=rss----c90dfb25088e---4</link>
            <guid isPermaLink="false">https://medium.com/p/179191d612b9</guid>
            <category><![CDATA[bluecadet]]></category>
            <category><![CDATA[developer]]></category>
            <category><![CDATA[google-drive-api]]></category>
            <category><![CDATA[electronjs]]></category>
            <category><![CDATA[nodejs]]></category>
            <dc:creator><![CDATA[Pete Inge]]></dc:creator>
            <pubDate>Thu, 04 Feb 2021 19:07:31 GMT</pubDate>
            <atom:updated>2021-02-04T19:16:02.057Z</atom:updated>
            <content:encoded><![CDATA[<p>An agency-wide move to Google Workspaces opened up a new set of problems for our team in maintaining project workflow.</p><h3>Moving to Google Workspaces</h3><p>Here at Bluecadet we recently upgraded to using Google Workspaces, which included the ability to use Google Drive to store all our project files. Previously, these files were scattered across an internal server, Dropbox, and individual Google Drive accounts. Our team created a flexible file structure template for our projects with directories for supporting our most common workflows and file shortcuts to key documents and files. Unfortunately, testing revealed a problem: a simple copy paste of the parent directory did not preserve those shortcuts (they referenced the original directory in the template project, rather than the new one in the copied project).</p><h3>Shortcuts</h3><p>Let’s take a step back and look at what exactly a shortcut is and why it’s a problem. A shortcut is a link that references another file or directory. In a complex file structure it is sometimes handy to have a quick shortcut to another directory multiple levels or directories away. For example, if you have a directory containing notes for developers, it might be nice to have a shortcut to that same folder under your client discovery directory.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7_uU1bZf838mrx8nc0xNOw.png" /><figcaption>A sample directory structure.</figcaption></figure><p>However, when you copy the file structure, the shortcut remains pointing to the directory in the template structure and not the new directory in your new project.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pxlF0ed63EEbFhW6E2rSxw.png" /><figcaption>Copy structure for a new Project</figcaption></figure><p>Our Google rep suggested that we use the API to help maintain the directories and shortcuts when copying them to a new project folder. And this is where I entered, tasked to make it all work! As a web developer typically deep in PHP code, I was excited to dive into something a little different, the Node.js world.</p><h3>Breaking down the problem</h3><p>Here is our little project scope: We had a specific shared drive. In that shared drive we had a directory that contained multiple templates, depending on project type, with each containing about 80 directories and shortcuts. So, I had to be able to copy any of these templates. The templates were a work in progress so it was important to be able to just copy and pate the template structure in Google Drive, instead of hard-coding the templates in a config file. Oh and, of course, it had to be done ASAP so we could get the team using the new structure.</p><figure><img alt="Google drive template folder structure" src="https://cdn-images-1.medium.com/max/877/1*oRjW1DP2wNvm454_PyKmxQ.png" /><figcaption>The proposed template directory structure.</figcaption></figure><p>The first step was just getting connected to the Drive API. In the past I’ve always had problems dealing with authentication and Google APIs, however, their <a href="https://developers.google.com/drive/api/v3/quickstart/nodejs">basic node.js example</a> got me up and running pretty quickly and to be honest that was not as terrible as I thought. Pleasant surprise!</p><h4>Directory wrangling</h4><p>Second step was simply getting a basic understanding of how Google treats files and directories. I played a lot. After I had about 20+ tabs open with pages from different parts of the Drive API documentation, I was getting pretty familiar with it and started to dive into my actual task, copying an entire directory structure. And now we move from the nice little contained Google examples into the fun of a real project. It never works as easy as their examples. I created a test directory structure, all 5 directories, 2 levels deep.</p><p>The first hurdle was efficiently getting the directory structure. There is no way to call the API and say give me all the children of a directory. I can query for EVERYTHING on a shared drive and extract what I need, or recursively work through the hierarchy. In order to limit my total API calls I chose the first option.</p><p>After that hurdle, it was a pretty straight forward process. Find a directory, copy it. And I was sort of done! Or so I thought…</p><h4>A long way to shortcuts</h4><p>The next step was to create the shortcuts. This is also pretty simple, except for the fact that the directories need to already be created. Now I had to manage ids; ids from the original template structure and the ids of the new structure and their relationships. Small hurdle, but added complication. But note that Google does provide a <a href="https://developers.google.com/drive/api/v3/reference/files/generateIds">handy method to generate IDs</a> so I can build this relationship structure before actually calling all the API endpoints to create directories and shortcuts.</p><p>At this point we are talking about a few hours of work, nothing crazy, and I am feeling really good about it and I take a step back. All of a sudden it hits me — since this is a Node script, how are project managers, the ones creating new projects, going to do this? I don’t want to be bugged every time we start a new project. And I definitely don’t want to have to try and train project managers on installing and running a NPM package. I can’t even imagine. Next hurdle.</p><h3>Creating a tool for the team</h3><p>There are a few options out there that I could have done, however I’ve been really interested in <a href="https://www.electronjs.org/">Electron.js</a> apps and its potential. So not getting any advice or discussing it with anyone, and thinking it would only take an hour or two, I went right in.</p><p>Needless to say, I went from dealing with hurdles to hitting a number of walls… but it was so much fun. I do like a challenge. And, if you are asking, of course I underestimated the 1 or 2 hours to convert it.</p><p>First major wall was getting Google authenticated within the Electron App. Electron is a browser but also, it’s not. And it doesn’t like playing with things outside of its little world. You know, security and all. I’m sure there was a better way to do it, but I eventually got it working. I do think this is what took me the most amount of time transferring everything to Electron.</p><p>The next wall for me was fully understanding how Electron’s main.js file interacts with the window you are actually seeing. Initially it was so perplexing. It also could have been that it was midnight and I was on hour 6 of what I was sure should have taken me an hour. Probably not though. Needless to say, when I took a break, let my brain recoup and actually <a href="https://www.electronjs.org/docs/tutorial/quick-start#main-and-renderer-processes">read the documentation</a>, it all started to become clear.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/406/1*AbJpztKwfMo9SP1C82rZUQ.gif" /></figure><p>The next steps were to build out the UI a little bit, and how a user would choose a template and enter the new project title. Nothing Earth shattering here, but it was really fulfilling to see this “thing” come to life. Command line tools are really cool and powerful, but all of it is so hidden. It is very underwhelming at times. I did all this coding, I run the process and all I get is a new blank command prompt. Yeah! It wasn’t much but adding the UI felt good.</p><p>Now, of course, I jumped the gun and told the higher ups it was done and working. They did want it ASAP. Also, do you remember I said my test was 5 directories? And the actual template structure was 80+? Well, 10 minutes before I go to show it off, I switch it over to the real template directory structure. And it stalls… nothing. No output (I threw in a lot of debug messages as it is creating the directories) after the first 10 directories.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pbJsfp99y_JcKOV2cU10SA.png" /></figure><p>Well, after my “oh crap” moment, and rescheduling some meetings, I went back in. So, of course, I tried creating way too many new projects to debug what was going on: Will this work_001, Will this work_002, How about this one_003, please God let this one work_004, WHY WONT THIS WORK_005, did that actually work or was I not paying attention_006. Since this was in the shared public drive, I did get a few people asking me about my mental health. I was fine, just tired of naming things Test ###.</p><h4>Error handling</h4><p>Unfortunately, I didn’t take as close a look at the error messages as I should have. Another note, set proper reject methods and messages for your js Promises. It could save you time when debugging. Apparently copying 5 directories you do not hit any of Google API’s rate limits. When copying 80+ you do hit those limits. That fixed, we were good to go, but just running slower in order to not hit the limit. Also to note, you have to make one call to the API for each directory creation, instead of sending all the data at once. Kind of annoying.</p><p>Long story short I had a lot of fun “playing” and building up my js skills and after a solid week’s worth of work, we had a little desktop (Mac only) app to copy directories in Google Drive. We plan on building it out a bit more in the future, and I look forward to the new challenges.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/240/1*pwG9q89VKVVlwzOyhD284w@2x.png" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=179191d612b9" width="1" height="1" alt=""><hr><p><a href="https://medium.com/bluecadet/google-drive-node-js-and-a-custom-electron-app-179191d612b9">Google Drive, Node.js, and a custom Electron app</a> was originally published in <a href="https://medium.com/bluecadet">Bluecadet</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>