Xastol – Book
#RIP is a generative book that showcases the use of the hashtag “#rip” on Twitter for October 21st, 2016. Using Temboo to gain easy access to the Twitter AI for collecting data, random tweets with the given hashtag are accessed and placed over 8-bit tombstones. In correlation with the randomly selected tweets are randomly generated backgrounds. The tombstones, on which the tweets are placed, are the same size and shape and flip horizontally every page (even pages vs. odd pages). Additionally, the background colors are randomly generated with dark color range to give off a somber feel that contrasts with with the general sarcastic mood of the tweets.
The idea for my book actually sprang up while in the studio. After coming up with many over-complicated ideas and trying to figure out how realistic those ideas were while facing the learning curve of basil.js, I was stumped. That’s when a friend (takos) went into detail about us suffering and how we were going to probably spend the entire night in the studio, I responded with “rip” and then got myself thinking about how the term is used in slang. Initially, the term comes from “R.I.P.” or “Rest In Peace”, generally used when expressing ones condolences to the end of another’s life. However, the term has evolved into the singular word “rip”, and is often used to express sympathy with minor incidents of negative connotation.
I.E.
Person 1: “I just stubbed my pinkie toe and it hurts.”
Person 2: “rip.”
To help this come to life, I created two programs: one that would scrape data from Twitter and one that would generate the visuals. After being introduced to the website Temboo, gaining access to tweets on Twitter became trivial, as the website provides code for accessing the Twitter AI and Search features. This was completed in Processing.
I wanted to create visuals that would connect the traditional and modern usage of the term and found that Processing would also be useful for this. To connect the usages, I decided to create an 8-bit tombstone against a dark background (modern visuals against traditional feel).
Lastly, I converted my information into .json files (also done through Processing), so that data would be easily transmitted into InDesign (using basil.js).
In terms of my final product, I am satisfied, but not fully pleased. I am satisfied with the outcome of my project, given my limited knowledge on formatting print media and limited window to work on the project due to other class projects. Mainly, I think it’s the aesthetics of the book that lack, compared to the concept itself. I think if given more time to work with basil.js and print media, I’ll be able to create a much more aesthetically pleasing and content full piece.
Book Link: http://www.blurb.com/bookstore/invited/6594146/096a0b83792047730ec84f6dc02ab9d8dcb10012
PDF Link: rip-xapostol
Github Link: https://github.com/xapostol/60-212/tree/master/GenBook-xastol
TWITTER DATA COLLECTION CODE
import com.temboo.core.*;
import com.temboo.Library.Twitter.Search.*;
// Create a session using your Temboo account application details
TembooSession session = new TembooSession("xastol", "myFirstApp", "WHATEVER YOURS IS");
void setup() {
// Run the Tweets Choreo function
runTweetsChoreo();
}
void runTweetsChoreo() {
// Create the Choreo object using your Temboo session
Tweets tweetsChoreo = new Tweets(session);
// Set inputs
String myQuery = "rip";
tweetsChoreo.setQuery(myQuery);
tweetsChoreo.setAccessToken("YOUR STUFF");
tweetsChoreo.setConsumerKey("THEIR STUFF");
tweetsChoreo.setConsumerSecret("SECRET THEIR STUFF");
tweetsChoreo.setAccessTokenSecret("SECRET YOUR STUFF");
// Run the Choreo and store the results
TweetsResultSet tweetsResults = tweetsChoreo.run();
// Print results
println(tweetsResults.getLimit());
println(tweetsResults.getRemaining());
println(tweetsResults.getReset());
//println(tweetsResults.getResponse());
String[] result = {tweetsResults.getResponse()};
println(result);
saveStrings(myQuery+".json",result);
}
VISUALS CODE
// Template Provided by Golan
// Xavier Apostol (Xastol)
// 60-212 (8:30 - 11:20am & 1:30 - 4:20pm)
// xapostol@andrew.cmu.edu
// Random Character Generation
import processing.pdf.*;
boolean bRecordingPDF;
int pdfOutputCount = 1;
float count = 1;
float changeFac = 40;
void setup() {
size(750, 750);
bRecordingPDF = true;
strokeWeight(1);
}
void keyPressed() {
// When you press a key, it will initiate a PDF export
bRecordingPDF = true;
count += 1;
if (count % 2 == 1) {
changeFac = 40;
} else {
changeFac = -40;
}
}
void draw() {
if (bRecordingPDF) {
float backGCol = 255;
float ranR = random(50);
float ranG = random(50);
float ranB = random(50);
background(255); // this should come BEFORE beginRecord()
beginRecord(PDF, "tombstone" + pdfOutputCount + ".pdf");
//--------------------------------------------------------------
//Make all drawings here.
noFill();
beginShape();
float tombX = width/2;
float tombY = height/2;
float tombW = tombX*1.15;
float tombH = tombY*1.5;
rectMode(CENTER);
noStroke();
/*
// Tombstone
fill(100,100,100);
rect(tombX,tombY, tombW,tombH);
rect(tombX,tombY, tombW-20,tombH+20);
rect(tombX,tombY, tombW-40,tombH+40);
rect(tombX,tombY, tombW-60,tombH+60);
fill(150,150,175);
rect(tombX+changeFac,tombY, tombW,tombH);
rect(tombX+changeFac,tombY, tombW-20,tombH+20);
rect(tombX+changeFac,tombY, tombW-40,tombH+40);
rect(tombX+changeFac,tombY, tombW-60,tombH+60);
*/
fill(ranR,ranG,ranB);
rect(width/2,height/2, width,height);
endShape();
//--------------------------------------------------------------
endRecord();
bRecordingPDF = false;
pdfOutputCount++;
}
}
INDESIGN CODE
#includepath "~/Documents/;%USERPROFILE%Documents";
#include "basiljs/bundle/basil.js";
var jsonString = b.loadString("clean_tweets_sensored.json");
var jsonData;
//--------------------------------------------------------
function setup() {
// Clear the document at the very start.
b.clear (b.doc());
// Initialize some variables for element placement positions.
// Remember that the units are "points", 72 points = 1 inch.
var titleX;
var titleY = 72;
var titleW = 1080;
var titleH = 72;
var captionX;
var captionY = b.height/2;
var captionW = b.width - b.width/2;
var captionH = 180;
var imageX = 6;
var imageY = 10;
var imageW = 72*7;
var imageH = 72*7;
var coverFileName = "images/tomb1.png";
var coverImage = b.image(coverFileName, imageX, imageY, imageW, imageH);
// Make a title page.
coverImage.fit(FitOptions.PROPORTIONALLY);
b.fill(0);
b.textSize(200);
b.textFont("MasonSansRegular","Normal");
b.textAlign(Justification.CENTER_ALIGN);
b.text("#RIP", 12,90,480,360);
b.textSize(30);
b.text("Xavier Apostol", 153,339,360,72);
b.text("Fall 2016", 153,369,360,72);
// Make a info page.
b.addPage();
b.textSize(36);
b.textFont("MasonSansRegular","Normal");
b.textAlign(Justification.CENTER_ALIGN);
b.text("A Generative Book Using Twitter Hashtags", 72,162,360,180);
b.addPage();
b.textSize(36);
b.text("For Friday October 21st, 2016", 72,191,360,121);
// Parse the JSON file into the jsonData array
jsonData = b.JSON.decode( jsonString );
b.println("Number of elements in JSON: " + jsonData.length);
// Loop over every element of the book content array
// (Here assumed to be separate pages)
for (var i = 0; i < jsonData.length; i++) {
// Create the next page.
b.addPage();
// Load an image from the "images" folder inside the data folder;
// Display the image in a large frame, resize it as necessary.
b.noStroke(); // no border around image, please.
var anImageBFilename = "background/" + jsonData[i].image;
var anImageFilename;
if (i % 2 == 0) {
anImageFilename = "images/tomb1.png";
titleX = -b.width/2 - 10;
captionX = b.width/2 - b.width/5;
} else {
anImageFilename = "images/tomb2.png";
titleX = -b.width/2 - 40;
captionX = b.width/2 - b.width/4 - 10;
}
var anImageBack = b.image(anImageBFilename, imageX - 6, imageY-10, imageW+1, imageH);
var anImage = b.image(anImageFilename, imageX, imageY, imageW, imageH);
anImageBack.fit(FitOptions.PROPORTIONALLY);
anImage.fit(FitOptions.PROPORTIONALLY);
// Create textframes for the "screenName" field.
b.fill(0);
b.textSize(30);
b.textFont("MasonSansRegular","Normal");
b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.CENTER_ALIGN );
b.text(jsonData[i].screenName, titleX,titleY,titleW,titleH);
// Create textframes for the "date" fields
b.textSize(20);
b.textFont("MasonSansRegular","Normal");
b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.TOP_ALIGN );
b.text(jsonData[i].date, captionX,titleY+108,captionW,captionH);
// Create textframes for the "text" fields
b.textSize(20);
b.textFont("MasonSansRegular","Normal");
b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.TOP_ALIGN );
b.text(jsonData[i].text, captionX,captionY,captionW,captionH);
};
// For even amount of pages.
b.addPage();
var endPageFileName = "background/tombstone1-page-001.jpg";
var endPage = b.image(endPageFileName, imageX - 6, imageY-10, imageW+1, imageH);
endPage.fit(FitOptions.PROPORTIONALLY);
}
// This makes it all happen:
b.go();
Video of the professor flipping through my book: