2d
https://broken.place/wp-content/uploads/2022/02/space-pod.glb
1
#FFFFFF
90
0
1
0
0
1
XR Music Playscapes
antpb · · Leave a Comment
2d
https://broken.place/wp-content/uploads/2022/02/space-pod.glb
1
#FFFFFF
90
0
1
0
0
1
antpb · · Leave a Comment
I use CircleCI to maintain code quality and automate the deploy process for many of the web applications I manage. I recently sought to achieve that same configuration for my Hubs Cloud instances. This tutorial assumes that you already have a CircleCI account and understand the deploy process of Mozilla Hubs. If you’d like info on deployment, check out this blog post before you get started to get an overview of the manual deploy process.
The process is as simple as the following:
.circleci
directory to root.yml
file inside of .circleci
Add a production branch to a private fork of Hubs. This is what will be used as the deployment branch for your live site. I like to keep one fork private for my production instances and a separate public instance mirrored for contribution or sharing code. The goal of this configuration is to allow the deploy process to initiate when a pull request is merged. You can use this same approach to automate any number of tasks.
Mozilla Hubs Cloud by default has a login script called login.js
that is used as a step in the npm run deploy
command. This command requires human input to provide the Hubs Cloud instance url and administrator email address. We can work around the human input in CircleCI by hard coding our information in a similar script. CAUTION, you don’t want to be showing off your admin email in a public fork so try to utilize circleci environment variables when defining your admin address. For this tutorial example, I’m plain texting them because it will be easier to understand.
Your login script will live in hubs/scripts
alongside the other npm scripts. It’s almost identical to the login.js with a few tweaks to the variables. Below is a copy of mine. Feel free to make your own flavor!
import readline from "readline"; | |
import { connectToReticulum } from "../src/utils/phoenix-utils"; | |
import Store from "../src/storage/store"; | |
import AuthChannel from "../src/utils/auth-channel"; | |
import configs from "../src/utils/configs.js"; | |
import { Socket } from "phoenix-channels"; | |
import { writeFileSync } from "fs"; | |
const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); | |
(async () => { | |
console.log("Logging into Hubs Cloud.\n"); | |
const host = "your.hubsinstance.com"; | |
if (!host) { | |
console.log("Invalid host."); | |
process.exit(1); | |
} | |
const url = `https://${host}/api/v1/meta`; | |
try { | |
const res = await fetch(url); | |
const meta = await res.json(); | |
if (!meta.phx_host) { | |
throw new Error(); | |
} | |
} catch (e) { | |
console.log("Sorry, that doesn't look like a Hubs Cloud server."); | |
process.exit(0); | |
} | |
configs.RETICULUM_SERVER = host; | |
configs.RETICULUM_SOCKET_PROTOCOL = "wss:"; | |
const socket = await connectToReticulum(false, null, Socket); | |
const store = new Store(); | |
const email = "[email protected]"; | |
console.log(`Logging into ${host} as ${email}. Click on the link in your email to continue.`); | |
const authChannel = new AuthChannel(store); | |
authChannel.setSocket(socket); | |
const { authComplete } = await authChannel.startAuthentication(email); | |
await authComplete; | |
const { token } = store.state.credentials; | |
const creds = { | |
host, | |
email, | |
token | |
}; | |
writeFileSync(".ret.credentials", JSON.stringify(creds)); | |
rl.close(); | |
console.log("Login successful.\nCredentials written to .ret.credentials. Run npm run logout to remove credentials."); | |
process.exit(0); | |
})(); |
Next, you’ll need to add this custom login script to package.json
so that the automation has a reference. In line 27 below, I added mine called login-bp
.
{ | |
"name": "hubs", | |
"version": "0.0.1", | |
"description": "Duck-themed multi-user virtual spaces in WebVR.", | |
"main": "src/index.js", | |
"license": "MPL-2.0", | |
"homepage": "https://github.com/mozilla/hubs#readme", | |
"repository": { | |
"type": "git", | |
"url": "https://github.com/mozilla/hubs.git" | |
}, | |
"bugs": { | |
"url": "https://github.com/mozilla/hubs/issues" | |
}, | |
"scripts": { | |
"start": "webpack-dev-server --mode=development --env.loadAppConfig", | |
"dev": "webpack-dev-server --mode=development --env.remoteDev", | |
"local": "webpack-dev-server --mode=development --env.localDev", | |
"build": "rimraf ./dist && webpack --mode=production", | |
"bundle-analyzer": "webpack-dev-server --mode=production --env.dev --env.bundleAnalyzer", | |
"doc": "node ./scripts/doc/build.js", | |
"prettier": "prettier --write '*.js' 'src/**/*.js'", | |
"lint:js": "eslint '*.js' 'scripts/**/*.js' 'src/**/*.js'", | |
"lint:html": "htmlhint 'src/**/*.html' && node scripts/indent-linter.js 'src/**/*.html'", | |
"lint": "npm run lint:js && npm run lint:html", | |
"login": "node -r @babel/register -r esm -r ./scripts/shim scripts/login.js", | |
"login-bp": "node -r @babel/register -r esm -r ./scripts/shim scripts/login-bp.js", | |
"logout": "node -r @babel/register -r esm -r ./scripts/shim scripts/logout.js", | |
"deploy": "node -r @babel/register -r esm -r ./scripts/shim scripts/deploy.js", | |
"undeploy": "node -r @babel/register -r esm -r ./scripts/shim scripts/undeploy.js", | |
"test": "npm run lint && npm run test:unit && npm run build", | |
"test:unit": "ava", | |
"stats": "rimraf ./dist && webpack --mode=production --json", | |
"spritesheet": "npm run spritesheet:system-action && npm run spritesheet:system-notice", | |
"spritesheet:system-action": "spritesheet-js -f json -p src/assets/images/spritesheets/ --padding 8 --divisibleByTwo -n sprite-system-action-spritesheet --powerOfTwo src/assets/images/sprites/action/*", | |
"spritesheet:system-notice": "spritesheet-js -f json -p src/assets/images/spritesheets/ --padding 8 --divisibleByTwo -n sprite-system-notice-spritesheet --powerOfTwo src/assets/images/sprites/notice/*" | |
}, | |
"ava": { | |
"files": [ | |
"./test/unit" | |
], | |
"sources": [ | |
"src/**/*.js" | |
], | |
"require": [ | |
"@babel/register", | |
"esm" | |
] | |
}, | |
"dependencies": { | |
"@fortawesome/fontawesome-svg-core": "^1.2.2", | |
"@fortawesome/free-solid-svg-icons": "^5.2.0", | |
"@fortawesome/react-fontawesome": "^0.1.0", | |
"@mozillareality/easing-functions": "^0.1.1", | |
"@mozillareality/three-batch-manager": "github:mozillareality/three-batch-manager#master", | |
"aframe": "github:mozillareality/aframe#hubs/master", | |
"aframe-rounded": "^1.0.3", | |
"aframe-slice9-component": "^1.0.0", | |
"ammo-debug-drawer": "github:infinitelee/ammo-debug-drawer", | |
"ammo.js": "github:mozillareality/ammo.js#hubs/master", | |
"animejs": "github:mozillareality/anime#hubs/master", | |
"buffered-interpolation": "^0.2.5", | |
"classnames": "^2.2.5", | |
"color": "^3.1.2", | |
"copy-to-clipboard": "^3.0.8", | |
"dashjs": "^3.1.0", | |
"dayjs-ext": "^2.2.0", | |
"deepmerge": "^2.1.1", | |
"detect-browser": "^3.0.1", | |
"draft-js": "^0.10.5", | |
"draft-js-counter-plugin": "^2.0.1", | |
"draft-js-emoji-plugin": "^2.1.1", | |
"draft-js-hashtag-plugin": "^2.0.3", | |
"draft-js-linkify-plugin": "^2.0.1", | |
"draft-js-plugins-editor": "^2.1.1", | |
"event-target-shim": "^3.0.1", | |
"form-data": "^3.0.0", | |
"form-urlencoded": "^2.0.4", | |
"history": "^4.7.2", | |
"hls.js": "^0.13.2", | |
"js-cookie": "^2.2.0", | |
"jsonschema": "^1.2.2", | |
"jwt-decode": "^2.2.0", | |
"lib-hubs": "github:mozillareality/lib-hubs#master", | |
"linkify-it": "^2.0.3", | |
"markdown-it": "^8.4.2", | |
"moving-average": "^1.0.0", | |
"naf-janus-adapter": "^4.0.1", | |
"networked-aframe": "github:mozillareality/networked-aframe#master", | |
"nipplejs": "github:mozillareality/nipplejs#mr-social-client/master", | |
"node-ensure": "0.0.0", | |
"pdfjs-dist": "^2.1.266", | |
"phoenix": "github:gfodor/phoenix-js#master", | |
"prop-types": "^15.7.2", | |
"raven-js": "^3.20.1", | |
"react": "^16.13.1", | |
"react-dom": "^16.13.1", | |
"react-emoji-render": "^0.4.6", | |
"react-infinite-scroller": "^1.2.2", | |
"react-intl": "^2.4.0", | |
"react-linkify": "^0.2.2", | |
"react-router": "^5.1.2", | |
"react-router-dom": "^5.1.2", | |
"screenfull": "^4.0.1", | |
"three": "github:mozillareality/three.js#hubs/master", | |
"three-ammo": "^1.0.11", | |
"three-bmfont-text": "github:mozillareality/three-bmfont-text#hubs/master", | |
"three-mesh-bvh": "^0.1.2", | |
"three-pathfinding": "github:MozillaReality/three-pathfinding#hubs/master2", | |
"three-to-ammo": "github:infinitelee/three-to-ammo", | |
"uuid": "^3.2.1", | |
"webrtc-adapter": "^6.0.2", | |
"zip-loader": "^1.1.0" | |
}, | |
"devDependencies": { | |
"@babel/core": "^7.3.3", | |
"@babel/plugin-proposal-class-properties": "^7.3.3", | |
"@babel/plugin-proposal-object-rest-spread": "^7.3.2", | |
"@babel/polyfill": "^7.4.4", | |
"@babel/preset-env": "^7.9.0", | |
"@babel/preset-react": "^7.0.0", | |
"@babel/register": "^7.0.0", | |
"@iarna/toml": "^2.2.3", | |
"acorn": "^6.4.1", | |
"ava": "^1.4.1", | |
"babel-eslint": "^10.0.1", | |
"babel-loader": "^8.0.5", | |
"babel-plugin-react-intl": "^3.0.1", | |
"babel-plugin-transform-react-jsx-img-import": "^0.1.4", | |
"copy-webpack-plugin": "^4.5.1", | |
"cors": "^2.8.4", | |
"css-loader": "^1.0.0", | |
"dotenv": "^5.0.1", | |
"eslint": "^5.16.0", | |
"eslint-config-prettier": "^2.9.0", | |
"eslint-plugin-prettier": "^2.6.2", | |
"eslint-plugin-react": "^7.10.0", | |
"eslint-plugin-react-hooks": "^4.0.0", | |
"esm": "^3.2.5", | |
"fast-plural-rules": "0.0.3", | |
"file-loader": "^1.1.10", | |
"html-loader": "^0.5.5", | |
"html-webpack-plugin": "^4.2.0", | |
"htmlhint": "^0.11.0", | |
"jsdom": "^15.1.1", | |
"localstorage-memory": "^1.0.3", | |
"mediasoup-client": "github:versatica/mediasoup-client#v3", | |
"mini-css-extract-plugin": "^0.8.0", | |
"ncp": "^2.0.0", | |
"node-fetch": "^2.6.0", | |
"node-sass": "^4.13.0", | |
"ora": "^4.0.2", | |
"phoenix-channels": "^1.0.0", | |
"prettier": "^1.7.0", | |
"protoo-client": "^4.0.4", | |
"raw-loader": "^0.5.1", | |
"request": "^2.88.2", | |
"rimraf": "^2.6.2", | |
"sass-loader": "^6.0.7", | |
"selfsigned": "^1.10.2", | |
"shelljs": "^0.8.1", | |
"spritesheet-js": "github:mozillareality/spritesheet.js#hubs/master", | |
"style-loader": "^0.20.2", | |
"stylelint": "^9.10.1", | |
"stylelint-config-recommended-scss": "^3.2.0", | |
"stylelint-scss": "^3.5.3", | |
"svg-inline-loader": "^0.8.0", | |
"tar": "^5.0.5", | |
"url-loader": "^1.0.1", | |
"webpack": "^4.32.2", | |
"webpack-bundle-analyzer": "^3.3.2", | |
"webpack-cli": "^3.2.3", | |
"webpack-dev-server": "^3.1.14", | |
"worker-loader": "^2.0.0" | |
}, | |
"optionalDependencies": { | |
"fsevents": "^2.1.3" | |
} | |
} |
Create a directory named .circleci
in the root of your repo and create a config.yml file inside that matches the below. Be sure to change the script name in line 36 to whatever you define for your login script.
version: 2 | |
jobs: | |
build: | |
docker: | |
- image: circleci/node:10-browsers | |
working_directory: ~/repo | |
steps: | |
- checkout | |
- restore_cache: | |
keys: | |
- v1-dependencies-{{ checksum "package-lock.json" }} | |
- v1-dependencies- | |
- run: npm ci | |
- save_cache: | |
paths: | |
- node_modules | |
key: v1-dependencies-{{ checksum "package-lock.json" }} | |
- run: npm test | |
- store_artifacts: | |
path: dist | |
deploy: | |
docker: | |
- image: circleci/node:10-browsers | |
working_directory: ~/repo | |
steps: | |
- checkout | |
- restore_cache: | |
keys: | |
- v1-dependencies-{{ checksum "package-lock.json" }} | |
- v1-dependencies- | |
- run: npm ci | |
- save_cache: | |
paths: | |
- node_modules | |
key: v1-dependencies-{{ checksum "package-lock.json" }} | |
- run: npm run login-bp | |
- run: npm run deploy | |
- store_artifacts: | |
path: dist | |
workflows: | |
version: 2 | |
build-and-deploy: | |
jobs: | |
- build | |
- deploy: | |
requires: | |
- build | |
filters: | |
branches: | |
only: production |
Push those changes to your production branch and you should be all set. Time to test!
Create a new branch named however you would like and make some visually different changes to your site. Once done, commit the changes and start a pull request against the production
branch. You should see that the PR is starting tests to make sure that the incoming changes are deployable.
When you’re ready to merge those changes, have your phone or mail client near because the script will initiate the AWS SES to email you a login request from CircleCI. Click the login link, and let CircleCI do the rest. After a while you’ll see the build is a success which means that you can check your live site to see the changes reflected.
You did it! Hopefully. If not, let me know in a comment where you struggled and I’ll see if I can help.
antpb · · Leave a Comment
I have some more useful learnings around customization of Hubs Cloud to share! If you want a refresher on what Hubs Cloud is, give my overview post a read. As the title suggests, this post is a technical overview on how to get WordPress REST data to inject html content in your Hubs Cloud home page. This is a very basic example, but explains how you can navigate local workflow, CORS, and utilize an API in Hubs.
The reason I set out to accomplish this is because I wanted a way to decouple the design aspects of a Hubs Cloud instance from the core tech. Simply, I want to give someone content creation power without the ability to break the app. WordPress has an amazingly rich content editor and a REST API that can serve that final markup. A perfect match! This very website you’re reading was my data source for the experiment.
Then end goal was the following image which illustrates WordPress managed content rendering below the site logo in a Hubs Cloud instance.
Prerequisites
I’ll assume you have the above ready to go. For this project, I’d recommend forking Hubs to track your changes. I’ll step through how to add a package and update your client to include and use that package. Have a terminal handy!
First, set up your custom client by creating a fork of Hubs and clone it locally to wherever you wish to work. From the root directory of the fork run npm ci
then npm run login
to connect your local up to your Hubs Cloud instance. Follow the prompts and click the sign in link sent to your email. If you want to see your local version, run npm run dev
to start the dev environment. Once compiled, you should be able to resolve your app by visiting https://localhost:8080/. This command will also watch for changes in the files so you can start development of new features.
Note: you may get a warning in your browser when attempting to access localhost via https. You might need to do some browser hacks to allow localhost to load securely without a valid cert. I use Firefox and set options to trust the url and proceed.
For the library to parse the REST response I used Axios as it was a super simple framework to set up. Add Axios to the dependencies section of your package.json
in the root of the repo.
"dependencies": {
"axios": "0.19.2",
After adding the package, I typically delete the node_modules
and package-lock.json
before running npm install
which regenerates the dependencies and a package-lock file. Running install will set your app up to use the Axios functions. After a successful install, run npm run dev
to start watching for changes again. Leave that open in the background and use it to debug/compile. Every file that is saved in your local will reinitialize a build. This should give you instant debug feedback.
The home-root.js file in the react component library of Hubs is where the rendering logic of the home page is located. I placed my markup in there, but feel free to be more organized and clever than me.
You’ll want to import axios into home-root.js
import axios from "axios";
Then add a state within the component for your markup. I named mine data
.
state = {
data: null,
dialog: null,
signedIn: null
};
Next, you’ll want to prepare your data by using the axios.get
function. I chose componentDidMount
to do this with. In the below example, I’m directly hitting the ID of my WordPress page endpoint to return the markup. Use setState
on data
as res.data.content.rendered
which will store the final rendered content area from your WordPress page.
componentDidMount() {
axios.get(`http://broken.place/wp-json/wp/v2/pages/2891`)
.then(res => {
this.setState({
data: res.data.content.rendered
});
})
In the below image, the content->rendered data is what we are using. Notice it is the markup of the end product image example at the start of this post.
In Hubs, the best place I found to render the markup of my page was in the hero functions named renderNonFeaturedRoomsHero
and renderFeaturedRoomsHero
.
Using the dangerouslySetInnerHTML
function, we can display the markup from our WordPress page inside the render.
<div
dangerouslySetInnerHTML={{
__html: this.state.data,
}}>
</div>
Here is a link to a diff between my changes and home-root.js master: https://github.com/mozilla/hubs/compare/master…antpb:headless-hubs-bp#diff-ca3a521596d66872026a7eff939c860dL1
If your custom client is rendering and compiled successfully, you may be ready to deploy. Hubs has a helper script to deploy using the login credentials from the command at the beginning of this post. To deploy, kick off npm run deploy
and sit back while the client and admin build. Once this script uploads and deploys, you probably wont see your changes on your Hubs Cloud instance. You may immediately notice the API content is nowhere to be found on the live instance! On to the fix and bane of development experience, CORS.
Cross-Origin Resource Sharing (CORS) is what validates that the content being pulled to your site is legitimate and trusted. The reason your markup is not populating is because the server needs configuration to be set to allow certain domains to provide resources. Hubs by default has lots of the groundwork to mange these trusted sources and even has a settings panel in the admin area to manage your own. Here’s where we fix our issue of content not rendering on our newly deployed custom client.
In the advanced tab of Server Setup ( /admin#/server-setup
) you will find a long list of customizable settings like headers, CORS, Javascript, etc.
Search for the field titled “Extra Content Security Policy connect-src Rules.” and also img-src
. In these sections you will need to add the source of your API. In my case, it is http://broken.place
.
Next, you’ll want to add your “Allowed CORS Origins” which is the last option at the bottom of the settings list. In this section you will want to list the domains you expect content from. Put a comma between each source. IMPORTANT: Be mega sure to include all the domains that your Hubs Cloud instance utilizes as well as the extra sources that your are receiving data from. In my experience, forgetting my backend and primary Hubs domain locked me out. Be warned!
Below is what mine looked like:
Once you’ve saved these options, clear your browser cache and you should start seeing your new custom homepage render!
I hope this article helps you understand how to configure and deploy your own custom client as well as interact with outside origins in your Hub. It goes without saying that this method can be applied to any custom functionality you add to your Hubs Cloud instance. I have some work to do still on utilizing the stylesheets from the parent domain to unify the styling. I also hope to build a content block in WordPress where you can define a Hubs room ID and render the number of participants in the room. This would give the ability to control the layout of featured rooms on the home page!
antpb · · 4 Comments
Social VR is an incredible opportunity to transform our websites into physical spaces! The Mozilla Hubs team has developed a way to give individuals the power to host and control custom 3D experiences from their own managed infrastructure. They call it Hubs Cloud. Of the recent advancements in VR tech, I’ve been most excited about the browser focused Mozilla Hubs social platform. VR and AR will be an impactful force to view our digital content (websites) as a “real” spaces and it is my view that XR is the next natural progression of the web. Mozilla’s commitment to making that experience open is exciting for many reasons. Probably most important is that anyone can build and control their own platform. I see the same opportunity as when WordPress pioneered the CMS space all those years ago!
Hubs is a social VR platform that allows folks to join a space together through almost any device. It offers some unique ways to collaboratively experience media and does so with special care around privacy. Best of all, it’s open source and they’ve developed it in such a way that you can host your own instance! Why would you do that? Well, for me, I was rapidly developing proof of concept prototypes in the social VR focus. I simply made a fork of Hubs and started adding my custom bits to achieve prototypes. I spun my first instance up November 2019 and, (humble brag detour) I was the first person spin one up successfully! Since then, I’ve been hosting the stack with very minimal costs and have done some amazing things with it. More on that in a bit.
If you have your doubts about VR barrier to entry, fret not as I’ve found the software is equally comfortable and immersive in the desktop non-headset experience. Liv Erickson wrote an excellent post on this topic that I highly recommend.
There are massive challenges with privacy in VR. It seems folks are gravitating towards wanting private versions of social services. Many people have lost their trust in social media companies. The sheer scale of risk in data shared from XR devices has highlighted a need for standards to be set in relation to privacy. Mozilla has been leading the charge in social VR and how it relates to privacy. Their approach in respecting user data and minimizing the information needed to have shared experiences in Hubs is commendable. I also appreciate that they have acknowledged there is a line where things you do in the public virtual space should be understood as being public no different from reality. This brings many debates on how we should represent ourselves in XR spaces but I’ll save that for a future blog post. It’s very early still, and I’m by no means an authority on this topic, but the focus on privacy will be prominent as folks join our experiences. It’s important to call out and consider early.
I configured my custom instance of Hubs Cloud on AWS early November 2019. I’ve seen an average cost of around $15 a month (using t3 micro.) The configuration was a very simple AWS CloudFormation template that offers the ability to set cost limits, assign keys, and set the size of server. My first instance was actually configured and taken live using my iPhone browser! In recent months I pumped my instance up to a medium size for peace of mind and that has increased my monthly cost by ~$5.
There’s additional config options in Hubs admin to save money using Cloudflare workers. Be sure to set that up early if you anticipate many or large assets. Here’s a link to the cost break down.
I have been primarily using my instance to experiment with live music performances in social spaces and also use it to showcase white labeled possibilities that brands may be able to utilize. The built in Spoke scene editor offers flexibility to design and manage spaces completely in the browser. In one of my more wild experiments, I built a native Unity app that broadcasted spherical panoramic video of a game to YouTube live. From there, the Hubs instance wrapped the youtube live video and audio stream around the audience in a 360 video textured skybox. The below video illustrates the proof of concept.
Test run of audio and video for a virtual performance I’m doing tomorrow at 5pm CST. I’ve now got the webcam of the instruments streamed into the Hubs room. Few spots left if anyone has a VR headset and wants to join. Will be steaming 2D to twitch also! pic.twitter.com/HjXP2hWuJa
— Anthony Burchell (@antpb) March 28, 2020
If you choose to get into the code and customize your Hubs instance, the deploy process is straight forward. It’s as simple as a single deploy command! The first thing I did when I configured my local environment was customize the home page. I suspect home page customization will get easier sooner but if you’d like to do this currently, editing the home-root component will get you there fast. I’ve been experimenting with using WordPress REST data to manage the content of my home page and SEO meta. Here’s a screenshot of that proof of concept. I’ll dive deeper on the topic in a future blog post. 😉
There are a few prerequisites that you should have ready. I struggled most on domain configuration. Don’t feel discouraged if you hit roadblocks in creating the stack; DNS is just hard. In my experience, it was easiest to have all domains controlled by AWS Route53, however, this is not a requirement. IMPORTANT: Follow this step by step guide when configuring up your instance. Below is just a synopsis of what you need to do. There are tiny steps between, so be sure to do the cooking by the book. 🙂
Domain Prerequisites:
.link
TLD for mine)There are many ways to configure your domains so be sure to check out the documentation around domain recipes to find what works best for you.
Miscellaneous Requirements:
The setup can all be done in a single CloudFormation template. When you’ve finished the QuickStart guide steps prerequisites, go to hubs.mozilla.com/cloud and fill out the stack information. It’s important that your SSL cert and other resources exist in in the same region. You can validate your region by clicking the top right corner dropdown in the AWS console. During the stack creation, you may get an email from AWS to verify some of the resources. Be sure to click those links.
If you are using a primary domain managed outside of AWS, you’ll need to create a CNAME pointing to the stack. You can find that CNAME value in the Cloudfomation “Outputs” tab after a successful creation. Look for the “AddressForRootDomain” field and point your primary domain via CNAME to the value provided. It should resolve!
When you’ve cloned the repo to your local be sure to install and build things fresh by running: npm ci
To connect your local copy to your instance run: npm run login
This script will verify your admin user via e-mail and grant your computer push capabilities to the instance. Be sure you are using the machine that you configured the keypair for.
To deploy your local copy, from the repo root run: npm run deploy
This task goes through the build process and deploys your local changes to the instance. When it’s complete you should be able to hit the site and see your changes.
I’m going to keep posting in the broken.place blog about my explorations in Hubs. I’ll dive much deeper and will share what I learn through posts like this. There have been many learnings in the area of spatial audio, scene optimization, and various workflow tips that I’d love to share. Let me know if you have set up your own Hubs Cloud! I’m curious what others are interested in doing with this project.
antpb · · Leave a Comment
I woke up last Saturday and saw the below tweet by Stella Cannefax where she announced the 1.0 release of her OSC Library (compatible with Windows, MacOS, Android, and iOS.) I immediately needed to try and build something! To my delight, it took very little of my Saturday morning to integrate OSC components into the AR music interfaces of Broken Place AR.
open sound control core, 1.0
— we only need to be lucky once (@simulacracid) February 22, 2020
a new, polished version of my OSC library for Unity (2018.4+), with a stability guarantee.
* basic send/receive handling with no code
* simple API
* supports all OSC types
* kind to the garbage collector
* tiny https://t.co/Xq6fPkuqxF
Open Sound Control is a very popular messaging protocol with high fidelity options in broadcasting midi-like information. It is typically used in music production/performances, visual experiences, and so much more. For the sake of this morning project, I focused on making an AR controller for the Renoise music software. Renoise natively supports Open Sound Control and exposes much of the project control parameters through various routes. Below is a screenshot of the message options allowed in Renoise. You may notice that the messages available are very simple routes, for instance in /renoise/song/bpm
, BPM is given a value via the protocol; in this case numbers.
In the previous example of /renoise/song/bpm
the final parameter is listening for a certain value type, often a boolean or a number. Lucky for us, those types of values are very easy to find in Unity components and translate using the handy OscCore components. 😈
OscCore highlights that it is tiny and I think the greatest testament to that is how fast it was to configure. The system requires two things, a network object that is handling the sending of messages, and an object to define messages or routes to send values to. The later is what maps your game object values to a desired OSC message. Below is a screenshot of the primary “network” component assigned to it’s own root level game object in a standard AR Foundation scene. (Note, I’m using iOS build target.)
The OSC Sender component allows you to set the IP Address and ports that messages will be broadcast to. I plan to expose this option via player prefs in a later version of this project so that the user can define the IP address of their target computer. It’s important to note, if using this in a public setting, you should create your own private network to connect both your app and host computer. When you have both devices on the same safe network, you can find your target computer’s IP address in your system network settings and configure the component accordingly. This is where having a player pref option to set the IP will come in handy. ;D
Messages can be sent to a defined route using the Property Output component. This component is what maps a defined value of a component, in my case a VJUI knob, to a given route. I recommend placing these components on the same object that it is referencing. In my experimentation, I had a top level game object referencing multiple knobs and because of how my AR Foundation scene instantiated the prefab, it lost the references. So yeah, attach the component to the thing it is watching if it makes sense.
Below we have a close up screenshot of the Property Output component. You’ll notice that the component references the OSC Sender component, of which will be sending the messages defined. In the example of my knob, the Property Output component is attaching the /renoise/song/instrument/-1/macro3
route to the Knob’s value parameter. You may notice immediately that changing the value parameter sends the messages to the target app. Below are also some video tweets that show off my working proof of concept. Yay!
The latency is super not bad?! Uh, I think I have an AR performance controller for Renoise 😳 pic.twitter.com/bczlRDtokU
— Anthony Burchell (@antpb) February 23, 2020
Well shoot, I have a new fun project to chip away at! Something I want to try next is targeting two separate devices from the single AR app. Imagine that you have a computer hooked to a projector glitching a feed of the AR app’s perspective and another computer on a stage performing music. With a single controller, the performer can be in both places at the same time and maybe even unify the sound controls with visuals by attaching multiple Property Output components to a single UI component. I have so many ideas of things to build with OscCore. You should check it out and contribute back if you find improvements! I highly recommend giving the project’s README a glance before getting started. It gets deep into the code and makes my mind race with ideas. 😀 Thanks Stella!