Continuity for an Augmented world
Let’s preface this article with a bit of hand waving. Please assume that we live in a world 5 minutes into the future where we have figured out AR implants or non-ridiculous glasses. However, for now your mobile exo-brain(phone) will work.
I will begin by saying I have felt robbed for some time now. Something is missing. I was promised the future would be a late-stage capitalism state where I would be inundated with interactive 3d advertisements in a gloomy Phillip K. Dick urban sprawl. Granted we are close. I build and trade imaginary internet coins, work for a multinational tech conglomerate, carry a super computer in my pocket, and have a handful of nonhumans living in my house. But I know we can do better. Also <*handwaving intensifies*> in a more decentralized way! Free market persistent advertisements should be everywhere now. Since no one seems interested in making this a reality, I guess I will have to step my game up. Be the change.
On of the most common pitfalls of modern AR is the lack of continuity with actual reality and even itself. I believe this disconnect can be remedied with modern public blockchains. In order for the system to be robust enough for wide spread usage, it will need distributed file storage for rapid access of the advertisements, a public market for advertising space, and a simple recognizable target for the advertisement(to project on, obviously YOU are the real target). And it also goes without saying this will all need to be open source. With these requirements in mind: lets do this!
For this proof of concept we will be using a the Burns Capital . The success criteria will be if you, the reader, are able to see this logo on demand anywhere in the world. I am using a simple image for demo, however it is just as easy to make 3d objects and any other aframe.io objects using the same method. That will hopefully be covered in another article. Also, This is living code so it will be updated likely by the time you are reading this. The repo link is at the bottom, its open source, help yourself to the latest code base.
If your impatient; on your mobile, go here https://tinyurl.com/y8o5f4ml
and point your phone at this:
System overview
Before we get too deep into the coding aspect of the system lets briefly look at the network design. 1) A user is served a lightweight web application from ipfs, 2)the webpage uses a public api to access a 3) contract on the ETC network to get information about targets and 4) the advertisements are then retrieved from ipfs and 5) displayed at the target. I refer to this as zero weight architecture.
Dive deep!
File storage:
This one is the easiest of the four requirements. If you are unfamiliar with the interplanetary file system (IPFS), stop everything you are working on, learn about it, and start using it now. When a file is added to the ipfs, it is given a unique identifier. This identifier is used to distribute the file to other ipfs nodes and recall it as needed. Super useful in our case.
$ ./ipfs.exe add ~/Pictures/bclogo.pngadded QmRYrsq4uHk3AuVapdyoPtbF4zuzw7dbkc4r8HBKssB3De bclogo.png
The BC logo is now available EVERYWHERE!!!!!! Not super impressive, the internet has existed for some time now but we can now access our unique image using an ipfs API point on our machine or any other machine attached to the ipfs system using its id. The file is also publicly available at the ipfs api point is located at: https://ipfs.io/ipfs/QmRYrsq4uHk3AuVapdyoPtbF4zuzw7dbkc4r8HBKssB3De
Distributed access
Constantly passing around QmRYrsq…. is painful and scales poorly. Being a blockchain architect by trade, this is obviously the job for my baby: Ethereum Classic! We will need a contract which we are able to write to once, and read many times. It would also be nice if no one could over write my logo with out my permission. Other nice to have features include: public rating system like movies and games have, the ability to resell my ad space, and a one to one mapping. To do this we will create a structure and an enum.
enum ratings{G,PG,PG13,R,NC17}struct arTarget {string logo;bool owned;address owner;bool forSale;uint price;ratings rating;}
For simplicity in this demo, we will only be using uint8. This gives us 255 unique seats to map advertisements to which should be plenty to demonstrate the concept.
mapping (uint8 => arTarget) public targets;
A bit on targets
We will be using a 3x3 matrix format without error detection or correction, allowing up to 64 different markers to be identified. They 3x3 targets can be downloaded from this link for your convenience. Yes we are only using 64 of the 255 available seats. In a real future world production environment we can easily scale this up to many many more seats and use a slightly larger grid. The idea is to avoid overly complex structures like qr codes and keep it human readable and if need be drawable.
Reading data from a target
We will need a simple getter to allow users to access the stored information. The two most important pieces of information we will need are the image identifier and the content rating. Some sort of nanny monitoring software should be able to read the content rating and block sensitive viewers from seeing age inappropriate items if desired(out of scope).
function getLogo(uint8 _scan) public view returns(string ipfs, ratings rating){
ipfs = targets[_scan].logo;rating = targets[_scan].rating;return (ipfs, rating);}
Buying(and selling) a Seat
This is where things get a little tricky. We need a way for owners to update their seats, new users to buy seats, and both to buy and sell.
function updateLogo(uint8 _arID, string _ipfs) payable public returns(bool success){}
We will take an ID and a string with the unique identifier. This function is payable so we can allow users to buy seats. If everything goes well, we will return a successful result.
The easiest case, a seat is not owned and someone wants to occupy it; we let them. That would look like this:
if(!targets[_arID].owned){
targets[_arID].logo = _ipfs;targets[_arID].owned = true;success = true;}
To save space we will combine the actions that an owner can do and what a stranger can do to an unowned asset. Just pass everything you need to claim a seat
if(targets[_arID].owned && msg.sender == targets[_arID].owner || !targets[_arID].owned){
targets[_arID].logo = _ipfs;targets[_arID].forSale = _forSale;targets[_arID].price = _price;success = true;}}
If a seat is for sale and a stranger sends enough ether to buy the seat, we will pay the owner and change the seat info.
if(targets[_arID].owned && msg.sender != targets[_arID].owner && targets[_arID].forSale && msg.value >= targets[_arID].price){
targets[_arID].owner.transfer(msg.value);targets[_arID].owner = msg.sender;targets[_arID].logo = _ipfs;targets[_arID].forSale = _forSale;targets[_arID].price = _price;success = true;}
Simple enough for this use 😊
The Police!
As I have no desire to host a distributed crush film repository or any other hyper-objectionable content we will also add a rating review board and give them the ability to remove content. They are free to make their own system if they like, but I won’t be complicit in supporting it. So to accomplish this we will map the account address of our police union to a boolean variable(true for police, false for non police) and create a modifier we can add to some function that only the content police should be able to do. And in the spirit of transparency, we will publish an event whenever the pigs take something down. It will report the action(“removed item”), which public servant removed the content, and a comment string if we want to capture a reason.
mapping(address => bool) contentPolice;
modifier onlyContentPolice(){require(contentPolice[msg.sender]);_;}event squeal(string action, address officer, string comment);
To give our thought police something to do we will give them this function:
function clearSeat(uint8 _seat, string _comment) public onlyContentPolice returns(bool success){
targets[_seat].owned = false;targets[_seat].logo = “”;emit squeal(“removed”, msg.sender, _comment);return true;}
Now we have a short public storage database contract for our advertisements!
Compile and deploy using your favorite blockchain interface and were ready to go!
The AR part!
Welcome to the world of tomorrow! Aframe.io and AR.js will be doing all the heavy lifting for us. These opensource projects are working to bring vr/ar to the masses by building out the webVR frame work. For a quick intro on making an AR project with 10 lines of code check out: http://aframe.io/blog/ar.js
<disclaimer: I’m not a frontend developer>
We will be adding web3.js to the ar demo so we can interact with our blockchain. To do that we need to add the following to the html:
<script type=”text/javascript” src=”https://rawgit.com/ethereum/web3.js/develop/dist/web3.min.js"></script>
<script>console.log(“ o Establishing WEB3”)var uri = ‘http://localhost:8545';var web3 = new Web3(new Web3.providers.HttpProvider(uri));console.log(web3);
Of course; changing the uri to your preferred api point. For the demo page I will be using the etc commonwealth public api point. Add the contract logic :
console.log(“ o Setting up contract”)
var contractAddress = “0x9868a675D36E2554F559771539F00Ed188A33e69”;
var abiArray =[{"constant":false,"inputs":[{"name":"_seat","type":"uint8"},{"name":"_comment","type":"string"}],"name":"clearSeat","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_arID","type":"uint8"},{"name":"_ipfs","type":"string"},{"name":"_forSale","type":"bool"},{"name":"_price","type":"uint256"},{"name":"_rating","type":"uint8"}],"name":"updateLogo","outputs":[{"name":"success","type":"bool"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"_scan","type":"uint8"}],"name":"getLogo","outputs":[{"name":"ipfs","type":"string"},{"name":"rating","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint8"}],"name":"targets","outputs":[{"name":"logo","type":"string"},{"name":"owned","type":"bool"},{"name":"owner","type":"address"},{"name":"forSale","type":"bool"},{"name":"price","type":"uint256"},{"name":"rating","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"contentPolice","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"action","type":"string"},{"indexed":false,"name":"officer","type":"address"},{"indexed":false,"name":"comment","type":"string"}],"name":"squeal","type":"event"}];
var arContract = web3.eth.contract(abiArray).at(contractAddress);
When the viewer loads we want to call the “getLogo” function to get our advertisements and assign them to their respective targets. If no one is the owner of an object, we will show a “for sale sign”
console.log(“ o Assigning assets”);
for( i = 0; i < 6; i++){console.log(“ response from contract”+arContract.getLogo(i)[0]);if(arContract.getLogo(i)[0] != “”){document.getElementById(“tgt”+i).innerHTML = “<a-box src = \”https://gateway.ipfs.io/ipfs/"+ arContract.getLogo(i)[0] + “\”></a-box>”;} else {document.getElementById(“tgt”+i).innerHTML = “<a-box src = \”https://gateway.ipfs.io/ipfs/QmVGMspGVeNB4tyVPGToUXnGoaqg3iKgYts1DMq24pujfV\"></a-box> “;}}
More to come
This is a very simplistic system for demonstration purposes. Don’t let its simplicity fool you though, this is a live system on the public block chain. The contract can be found and interacted with at: 0x9868a675D36E2554F559771539F00Ed188A33e69 using the abi in the github repo.
Cheers!