annotatAR
lo-fi augmented reality for the mobile web
Rachel Boyce
Digital Media Capstone Studio, Fall 2015
Harvard Extension School
"Digital culture potentially makes us members of a shared virtual universe of knowledge, the common fostering of which will make the social tie the most important currency in future society."
Simon Lindgren, New Noise
Computer-mediated communication
Computer-mediated communication
-
Immediate
-
Networked
-
Publicly broadcast
-
Self-curated
-
Multi-sited
Computer-mediated communication
“Art makes data sticky."
Laurie Frick
Vietnam Veterans Memorial, Maya Lin
https://en.wikipedia.org/wiki/Vietnam_Veterans_Memorial
Net_dérive, Petra Gemeinboek
http://www.impossiblegeographies.net/net_derive/
#flowersforfreedom, Ai WeiWei
https://c1.staticflickr.com/3/2928/14597243737_01ab66973a_b.jpg
NO AD, RE+PUBLIC
https://play.google.com/store/apps/details?id=com.republic.NOAD
Invisible Monument,
Halsey Bergund & Lara Baladi
http://invisiblemonument.com/
annotatAR
twitter-augmented reality
AnnotatAR
Twitter API
#HashTag
userid
tweet text
timestamp
Database
Collection
userid
tweet text
timestamp
meteor.js [server]
Mobile site
Website
Information about project
Map of deployments
Data visualization (in future)
Camera input
Tweet overlay
Screen capture
Data download
URL
annotatar.xyz
Server-side
Meteor.startup(function () {
Hashtags.remove({});
Tweets.remove({});
//**** hashtags **/
Meteor.call("addHashtag", 42.37, -71.12, "harvardclimate");
Meteor.call("addHashtag", 48.86, 2.37, "cop21");
Meteor.call("addHashtag", 48.86, 2.38, "cop21");
Meteor.call("addHashtag", 40.71, -74.01, "ows");
Meteor.call("addHashtag", 40.70, -74.01, "occupywallstreet");
Meteor.call("addHashtag", 40.76, -73.99, "riseupoctober");
Meteor.call("addHashtag", 40.73, -74, "NYCStandsWithMinneapolis");
Meteor.call("addHashtag", 40.73, -73.99, "Justice4Jamar");
Meteor.call("addHashtag", 42.35, -71.06, "occupyboston");
Meteor.call("addHashtag", 42.35, -71.05, "occupyboston");
var hashtagsCursor = Hashtags.find();
hashtagsCursor.map(function(h){
console.log("pushing: ", h.hashtag);
hashtags.push(h.hashtag);
})
Twit = new TwitMaker({
consumer_key: Meteor.settings.twitter.consumer_key
, consumer_secret: Meteor.settings.twitter.consumer_secret
, access_token: Meteor.settings.twitter.access_token
, access_token_secret: Meteor.settings.twitter.access_token_secret
});
//*** REST
var handleTweets = Meteor.bindEnvironment(function(err, data, response) {
console.log(data);
console.log("***********************", err, "***********************");
for(var i = 0; i < data.statuses.length; i++){
Meteor.call("addTweet", data.statuses[i].text, data.search_metadata.query, data.statuses[i].created_at);
}
});
//**************************************************//
// ****** uncomment to turn the rest on: ****** //
hashtags.map(function(h){
hashtag = h;
Twit.get('search/tweets',
{
q: h,
count: 50
}, handleTweets);
Client-side: hashtag subscription
try {
if (navigator.geolocation){
navigator.geolocation.getCurrentPosition(function(position){
navLat = position.coords.latitude;
navLon = position.coords.longitude;
navLat = Math.round(100*navLat)/100;
navLon = Math.round(100*navLon)/100;
console.log("lat: ", navLat, " long: ", navLon, " accuracy: ", position.coords.accuracy);
$("#dynamsg").append('<p>latitude: '+navLat+' longitude: '+navLon+' accuracy: <span id="acc">'+position.coords.accuracy+'</span></p>');
Meteor.call("findHashtag", navLat, navLon, function(err, result){
if(result){
hashtag = result;
console.log("subscribing to: ", hashtag);
Meteor.subscribe("tweets", hashtag);
Session.set("hashtag", hashtag);
}
else{
console.log("no hashtag found! subscribing to #occupy");
Meteor.subscribe("tweets", "occupy");
Session.set("hashtag", occupy);
}
});
});
}
else{
navLat = 0;
navLon = 0;
console.log("no geolocation detected");
}
} catch(err){
console.log("geolocation error: ", err);
}
Client-side: render video & tweets
try{
navigator.getUserMedia = navigator.getUserMedia || navigator.mozGetUserMedia || navigator.webkitGetUserMedia;
window.URL = window.URL || window.mozURL || window.webkitURL;
navigator.getUserMedia({ audio: false, 'video': true},
function(stream){
video.src = window.opera ? stream : window.URL.createObjectURL(stream);
video.play();
},
function(err){
console.err("video capture error: ", err);
})
} catch(err){
console.log("navigator.getUserMedia error: ", err);
}
video.style.position = "absolute";
video.style.visibility = "hidden";
setInterval(function(){
if(ffmobile){
var wh = window.innerHeight - video.height;
context.save();
context.scale(1, -1);
context.translate(0,-wh);
var img = context.drawImage(video, 0, -video.height, window.innerWidth, window.innerHeight);
context.restore();
renderTweets();
}
else if(mobile){
renderWarning("Browser not supported.");
}
else{
var img = context.drawImage(video, 0, 0, window.innerWidth, window.innerHeight);
renderTweets();
}
//*********
var renderTweets = function(){
var tweets = Tweets.find({}, {sort: {createdAt: -1}}).fetch();
if(!tweets.length) {
console.log("no tweets");
return;
}
var ageMax = 0;
tweets.map(function(data){
var age = parseInt(Date.now() - data.tweetCreatedAt);
if (ageMax < age){
ageMax = age;
}
});
var i = 1;
tweets.map(function(data){
i+=1;
var age = parseInt(Date.now() - data.tweetCreatedAt);
var fsizeMax = 50,
fsizeMin = 0;
if (age > ageMax){ age = ageMax };
// the scale needs to be non-linear...
var fsize = Math.floor((((fsizeMin-fsizeMax)*age)/ageMax)+fsizeMax);
alphaMax = 1.0;
alphaMin = 0;
var alpha = (((alphaMin-alphaMax)*age)/ageMax)+alphaMax;
fsize = 30;
var alpha = 0.9;
context.font = fsize+'px "Walter Turncoat"';
context.fillStyle = 'rgba('+data.color.r+','+data.color.g+','+ data.color.b+','+ alpha+')';
context.fillText(data.text, data.xPos+offset.x, data.yPos+offset.y);
}); // end tweets.map
$("#hashtag").html("<p>"+Session.get("hashtag")+"</p>");
$("#tweetBtn").href("https://twitter.com/home?status=%23"+Session.get("hashtag")+"%20%23annotatAR");
}; // end renderTweets
What do we achieve with lo-fi augmented reality for the mobile web?
Détournment or re-taking of public space with its corresponding virtual settlement
Provide an outlet for participation and action in a non-disruptive, discursive context
Amplify dissenting voices from people who are geographically and demographically diverse that unite around ideological issues
Utilize emerging web technologies like Meteor.js for non-business purposes
- Data on the wire
- Quick deployment
- Participation in the development community
Developing for a moving target
can i use getUserMedia?
http://caniuse.com/#search=getusermedia
Chrome
getUserMedia deprecation without HTTPS
does not prompt user to select camera on mobile device, defaults to selfie-cam
Firefox
inconsistent documentation and surprising bugs
iOS
no native support for WebRTC
https://www.flickr.com/photos/tensafefrogs/728581345/
"A rhizome never ceases to connect semiotic chains, organizations of power, and events in the arts, sciences, and social struggles.
The rhizome is something altogether different, a map and not a tracing.
Make maps, not tracings."
Gilles Deleuze & Félix Guattari, On the Line
Thank you!
annotatAR - Capstone Studio Presentation
By arebe
annotatAR - Capstone Studio Presentation
Final presentation for DGMD-E599 Digital Media Design Capstone Studio, Fall 2015.
- 1,718