AR Naruto summoning scrolls, created as an homage to my favorite childhood anime.

In Naruto, scrolls are used to store objects for later use. When it's unsealed, the object appears from the scroll. You can seal anything, from weapons to energy, to creatures. I saw this as a perfect opportunity to use Vuforia. I wanted to do this as a sort of wish fulfillment. For lots of us as kids (and adults), being able to mimic the abilities of our favorite characters is great fun. I searched for various scrolls used in the show, for example:

Image result for naruto scroll

Image result for naruto weapon scrollImage result for naruto water scroll

I re-edited them from 360p screenshots to HD for printing, and handmade the scrolls from discount paper and wooden sticks. Using Unity and a lot of tutorials, I created the fire and water from particle systems and grabbed the shuriken from online archives. I found this concept's alignment with the assignment very amusing because of how well they fit together. I was pleasantly surprised how well the scrolls worked as Vuforia image targets. One thing I wish I could change is that in Unity, I set a delay so that the object would appear after the glowing ring, but it didn't translate with the build to the Pixel phone.

Thank you to my friends for fooling around with this concept and indulging my love for Naruto.


spooky skeleton found in my closet

In this project, i toyed with the phrase "skeletons in the closet" and used Unity to make the idiom an (augmented) reality. I've established a relationship between the skeleton and the closet by confining the figure within the space. With its movement, the skeleton looks trapped and anxious - even inquisitive of its surroundings. It glances at the viewer and looks away with secretive movements. I wanted to also make it move out of the closet to symbolize the revealing of one's secrets but was not familiar enough with Unity and animations to implement this idea.

skeleton model


You ever feel like you really need to get some work done but the dang trackpad robots just won't give you no room?

Lately I've been feeling pretty burnt out from my workload so I decided to turn this AR project into a fun literal interpretation of my resistance towards doing any more work this semester.

The "site" is the laptop I use for schoolwork, on my desk which is also where I do my schoolwork. The "viewer" is me, a college student hopelessly behind on his deliverables, and the "object" is a bunch of happy-go-lucky robots freeloading my precious trackpad real-estate (good thing I brought a mouse!).

I modeled and rigged the robots in Blender, and applied different looping mocap animations I found on Turbosquid.


Shoe sweet shoe.

The video is made with XR Remote, but I had a lot of technical difficulties so I couldn't do everything I wanted to do with the project.I really wanted to let the viewer explore the shoe in 3D, walk around, get in close to see small details about the shoe. I also wanted you to be able to tap the screen and set the shoe on fire.

The point was to have the user engage with a kind of ordinary object, and make them do it in an unconventional space(close to the ground).

Linux stuff:
Build & Run Settings: Gradle build system.
Export to apk first, then run apk on phone.

Open terminal, navigate to where the .apk file is located.
adb install .apk
Or drag into phone directory and install from file manager on phone.


Activating GameObjects:

Awake and Start:


Conventions and Syntax:

Delta Time:


Enabling and Disabling Components:

Get Axis:

If Statements:



Look At:



Scope and Access Modifiers:

Behavior Components:

Switch Statements:

Translate and Rotate:

Update and FixedUpdate:

Variables and Functions:


Link to google drive:

"Recipes for the Mad..." is a recipe book that requires one to bring out their inner madness.

My initial inspiration for this project was from the bouba/kiki effect, in which people generally tend to link sharper sounds with more jagged shapes and softer sounds with more curvy shapes. (more information here: Bouba/kiki effect) I wanted to somehow split different ingredients into their "softness" category, for example, jelly would be soft and peppercorn would be sharp. I ended up changing my idea slightly so that, even though I had a mapping similar to that, it was based on the sweetness levels of an ingredient. Dream whip would be sweet and ghost pepper would be spicy.

With the grammar, I started off with very tame and mild sentences like "chop the pepper and stir it in a bowl" but I ended up getting super carried away with it. Sentences like "Lick the pepper and blend into the shape of a komodo dragon" or "Listen to the jelly and challenge it to battle until it becomes evil" became common sentences and I really enjoyed seeing what other funny responses it created. I created different arrays of different spiciness level ingredients and counted the net spiciness level of the overall product. I tried using that to create the relevant titles and color palette, but I ran out of time for it to work properly. I also wish that I made the generated patterns inside the pot more complex, like using the sandpiles tutorial from Dan Shiffman. At the end of the instructions, I wish there was more of a linking sentence like "now put all the ingredients together and boil in a pot."

Overall, however, I think this was one of the most fun projects I've worked on and I ended up fixing the color palettes/title based on sweet/spiciness after the book was due. Here are some pictures of recipes from my revised code:

Sweet example recipe:

Spicy example recipe:

import processing.pdf.*;
import rita.*;
PFont font;
int count = 0;
RiGrammar rg1;
RiGrammar rg2;
RiGrammar rg3;
float speed;
String[] numerics = {
  "1 and a half ",
  "2 ",
  "2 and a half ",
  "3 ",
  "3 and a half ",
  "4 ",
  "4 and a half ",
  "5 ",
  "5 and a half ",
  "6 ", 
  "6 and a half ",
  "50 ",
  "100 ",
  "1000 ",
  "500 ",
  "5000 ",
  "10 and a half ",
  "50 and a half ",
  "A quarter ",
  "A third "
String[] measurement = {
  "tablespoons of",
  "teaspoons of",
  "grams of",
  "liters of",
  "kilograms of",
  "fluid ounces of",
  "cups of",
  "pints of",
  "quarts of",
  "gallons of"
void setup()
  font = createFont("AlexandriaFLF.ttf", 12);
  size(432, 648, PDF, "chaine.pdf"); //6 x 9
void draw()
  PGraphicsPDF pdf = (PGraphicsPDF) g;
  PGraphics maskImage;
  PGraphics sourceImage;
  StringList all_ingredients;
  all_ingredients = new StringList();
  int num_ingredients = int(random(4, 9));
  int count2;
  String final_string = "";
  String title = "";
  //int count3;
  for (count2 = 0; count2 < num_ingredients; count2++) {
    rg1 = new RiGrammar();
    rg1 = rg1.loadFrom("recipes.json", this);
    rg2 = new RiGrammar();
    rg2 = rg2.loadFrom("recipes2.json", this);
    rg3 = new RiGrammar();
    rg3 = rg3.loadFrom("recipetitle.json", this);
    String var1 = rg1.expand();
    String var11 = rg2.expand();
    String var111 = rg3.expand();
    String ingred = " ";
    int rand_ing = int(random(0, 5));
    if (rand_ing == 0) {
      ingred = ultra_soft_ingredients[int(random(0, ultra_soft_ingredients.length))];
    else if (rand_ing == 1) {
      ingred = soft_ingredients[int(random(0, soft_ingredients.length))];
    else if (rand_ing == 2) {
      ingred = neutral_ingredients[int(random(0, neutral_ingredients.length))];
    else if (rand_ing == 3) {
      ingred = sharp_ingredients[int(random(0, sharp_ingredients.length))];
    else if (rand_ing == 4) {
      ingred = ultra_sharp_ingredients[int(random(0, ultra_sharp_ingredients.length))];
    int rand_ing2 = int(random(0, numerics.length));
    int rand_ing3 = int(random(0, measurement.length));
    String list_ing;
    list_ing = numerics[rand_ing2] + measurement[rand_ing3] + ingred;
    RiString var2;
    RiString var22;
    RiString var3;
    RiString var33;
    var2 = new RiString(var1);
    var22 = new RiString(var11);
    var3 = new RiString(ingred);
    var33 = new RiString(var111);
    var2 = var2.concat(var3);
    var2 = var2.concat(var22);
    final_string = final_string + " " + var2.text();
    title = var33.text();
    int store1 = 8;
    int store2 = 8;
    PGraphics[] pgs = new PGraphics[store1];
    PGraphics[] pgs2 = new PGraphics[store2];
    if (count <= 16) {
      pgs[store1-1] = createGraphics(width,height);
      //PGraphics sourceImage;
      for (int i = 0; i < store1; i++) {
        pgs[i] = createGraphics(width,height);
        pgs2[i] = createGraphics(width, height);
        pgs[i].fill(int(random(0,255)),int(random(0,255)),int(random(0,255)), 30);
        pgs[i].ellipse(int(random(141, 291)), int(random(249, 399)), int(random(150, 300)), int(random(150, 300)));
        if (i == 0) {
          pgs2[i].fill(int(random(0,255)),int(random(0,255)),int(random(0,255)), 20);
          pgs2[i].ellipse(int(random(141, 291)), int(random(249, 399)), int(random(20, 50)), int(random(20, 50)));
        else {
          pgs2[i].fill(int(random(0,255)),int(random(0,255)),int(random(0,255)), 20);
          pgs2[i].ellipse(int(random(141, 291)), int(random(249, 399)), int(random(20, 50)), int(random(20, 50)));
      maskImage = createGraphics(width, height);
      maskImage.ellipse(width/2, height/2, 150, 150);
      ellipse(width/2, height/2, 160, 160);
      ellipse(width/2, height/2, 170, 170);
      text(title, 60, 65);
      text("Ingredients:", 60, 100);
      for (int i = 0; i < all_ingredients.size(); i++) {
        text(all_ingredients.get(i), 70, 110 + (i * 13), 400, 600);
      final_string = final_string.substring(1);
      text(final_string, 60, 470, 312, 628);
 //432 648
  if (count == 0) {
    text("Recipes for the Mad...", 250, 291);
    text("by chaine", 250, 321);
  if (frameCount == 16) {
  } else {
  count += 1;


Chapter Title:
"A-Z...or something like that"
A cautionary introduction to existential crisis for the timid teen, based on what the internet thinks Nietzsche and others have to say about it.




Gif(will fix later):

Interactive Version(updated):

I initially wanted to create some fake government documents or conspiracies, or fanfiction based on fake books. I liked the idea of a machine generating something that sounded like it could plausibly come from a crazy person, or a tween with bad writing. I ended up doing an alphabet book of existentialism, but with the same kind of mocking/cringe-viewing lens. I originally planned on using actual works of Nietzche and existential philosophers, but I thought it would make more sense to grab quotes from questionable sources like "101 Existential Quotes that'll Make you Question Everything." These included curated quotes from actual philosophers, but whether they were accurate or taken out of context, I didn't know. And that was the point. Unfortunately I couldn't find an elegant way to find it, so it was just Google Search and copy-paste.

My intention with the aesthetics was just to have something that looked images I used to find all over Facebook, of just "deep" thoughts stylized in an "edgy" way--so dingy white type-writer text on black it was.

In general, most choices were guided by my experience/appreciation of the "uses-irony-incorrectly, edgy teenager" stereotype. I wanted it to read like something a 13-year-old would make in their free time back in 2012, because I was probably that 13-year-old.

Evaluation of Results:
In my opinion, the results are, mixed. There are some gems that are nonsensical in just the right way("Beware of letters. They are gay.") or are common seemingly "deep" statements often found and shared on the internet. The concept is really simple, but I think there was a lot I could have done with it, had I had time to play around with it. I think it's fun to look through some pages, but maybe not a whole 26 pages.

Because I didn't fine tune it, there are a lot of bugs that make their way on to the page too, like occasionally the program thinks "an" and "such" are adjectives. Also, I attempted to purposefully use every letter in order, but I think it just came off as laziness. There are also a lot of repeats because I had a rather small data set to draw from.

I made something that appealed to my sense of humor, and I'm not sure if the humor or intentions translate to other people, or if it seems mean-spirited in the way it mocks a certain type of person( or if it seems mocking at all).

Extra thoughts:
I had some observations while I was making the project. After I posted the first link, I added in a few full works by Nietzche(because I assume that's most people's immediate association with existentialism). This kind of messes up the idea of getting just the information cherry picked by internet folk, but it actually turned out more varied sentences, and had the same effect. I forgot that, obviously, shoving complex and nuanced ideas into this simple and narrow template would produce comical contradictions and nonsense. I also thought of my program as a 3rd "dumbness" filter of sorts. First the internet gets their hands on the original pieces, and then it goes through my program, and then we get this nonsense.

Rita, for generating text, with rhyming template provided by Char Stiles.

var rhymes, word, data, wordy, tit, letter;
var numS;
var pagesArr = [];
var t = 0;
var tmax = 7000;
class Page {
	constructor(title, text){
  	this.title = title;
    this.text = text;
function setup()
  createCanvas(300, 300);
  textFont("Courier New");
  //lexicon = new RiLexicon();
  data = loadStrings('such_deep.txt');
 // words = data.split(' ');
  console.log('data loaded');
  //setInterval(findRhymes, 2000);
function keyPressed(){
  console.log("getting pages...");
function getPages(){
  console.log("getting pages...");
  for (var i=0; i&lt;26; i++){
    console.log("got rhymes");
		var p = new Page(tit, rhymes);
    pagesArr[i] = p;
  var jsonObj = {};
  jsonObj.pages = pagesArr;
  //saveJSON(jsonObj, 'angst_pages.json');
  createButton('SAVE THE ANGST')
    .position(width/2, height/2)
    .mousePressed(function() {
    saveJSON(jsonObj, 'angst_pages.json');
function draw()
  text("hit  to deprive\nyourself of some meaning.",10,height-30);
	if(data == undefined){
  text(tit, 30, 40);
  text(rhymes, 280, 100);
function newWord(){
  console.log("new word...");
  var w = data[floor(random(data.length))];
  if (!w)
    return "";
  w = w.split(' ');
    console.log("split word...");
  w = w[floor(random(w.length))];
  w = RiTa.stripPunctuation(w);
  return w;
function numSyllables(w){
  var syll = RiTa.getSyllables(w);
  var count = 0;
  for (var x = 0; x &lt; syll.length; x++){
    if (syll.charAt(x) == '-')
  return count;
function rightWord(w){
	return RiTa.isAdjective(w) &amp;&amp; 
     //w.charAt(0) == word.charAt(0) &amp;&amp;
		!RiTa.isAdverb(w) &amp;&amp; 
		!RiTa.isNoun(w) &amp;&amp;
    (w != 'an') &amp;&amp;
    (w != 'such') &amp;&amp;
		!RiTa.isVerb(w) ;
    //(numS == numSyllables(w)) &amp;&amp; 
function findRhymes() { // called by timer
	if(data == undefined){
      console.log('undef data');
  	word = newWord();
  } while(!RiTa.isNoun(word));
  word = RiTa.singularize(word).toLowerCase();
  numS = numSyllables(word);
  letter = word.charAt(0);
  console.log("got word");
  do {
  	wordy = newWord();
  } while(!rightWord(wordy) &amp;&amp; t&lt;tmax);
  //wordy = RiTa.singularize(wordy).toLowerCase();
  wordy = wordy.toLowerCase();
    console.log("got wordy");
  tit = letter.toUpperCase() + " is for\n " + word.toUpperCase() + ".";
  rhymes = "Beware of " + RiTa.pluralize(word) + ".\nThey are " + wordy + ".";

Basil.js, for creating the pages in InDesign

#include "../../bundle/basil.js";
// Version for basil.js v.1.1.0
// Golan Levin, November 2018

function makePage(i) {
  // Load a JSON file containing your book's content. This is expected
  // to be located in the "data" folder adjacent to your .indd and .jsx. 
  var j = i + 1;
  var jsonString;
  if (j > 9)
    jsonString = b.loadString("angst_pages_" + j + ".json");
    jsonString = b.loadString("angst_pages_0" + j + ".json");

function makeSamplePage(){
  jsonString = b.loadString("angst_pages_sample.json");

function makePage_fromFile(jsonString){
  // Clear the document at the very start. 
  b.clear (b.doc());

  // Parse the JSON file into the jsonData array
  var jsonData = b.JSON.decode( jsonString );
  //var poems = jsonData.poems; 
  var poems = jsonData.pages; 
  b.println("Number of poems: " + poems.length);

  // Initialize some variables for element placement positions.
  // Remember that the units are "points", 72 points = 1 inch.
  var inch = 72;

  var titleW = inch * 5.0;
  var titleH = inch * 5;
  var titleX = (b.width / 2) - 3*(titleW / 5) ; // centered 
  var titleY = inch * 1.0;

  var textX = inch * -2; 
  var textY = inch * 5.0;
  var textW = inch * 7.0;
  var textH = inch * 3.0;

  // Loop over every element of the book content array
  // (Here assumed to be separate pages)
  for (var i = 0; i < poems.length; i++) {
      // draw the background first
    var r = 3/4;
    b.rect(b.width/2, b.height/2, r*b.width, r*b.height);
    // Format and display the "title" field.
    var poemTitle = poems[i].title;
    b.textFont("Special Elite","Regular"); 
    b.textAlign(Justification.CENTER_ALIGN, VerticalJustification.CENTER_ALIGN );
    var thePoemTitleFrame = b.text(poemTitle, titleX,titleY,titleW,titleH);
    var titleWords = b.words(thePoemTitleFrame);
    b.typo(titleWords[0], 'pointSize', 150);
    b.typo(titleWords[titleWords.length-2], 'underline');

    // Format and display the poem's "text" field. 
    // This time, let's do some typographic tricks.
    //b.textFont("Special Elite","Regular"); 
    b.textAlign(Justification.RIGHT_ALIGN, VerticalJustification.CENTER_ALIGN );
    // Get the text of the i'th poem
    var poemText = poems[i].text;
    // Create an InDesign TextFrame from this text
    var thePoemTextFrame = b.text(poemText, textX,textY,textW,textH);
    // Fetch the individual words in this TextFrame; 
    // Underline the first (0'th) word. 
    var poemWords = b.words (thePoemTextFrame);
    //b.typo(poemWords[0],'pointSize', 100);

    // Create the next page. 
    if (i < (poems.length-1)){ b.addPage(); } } // Ensure that there are an even // number of pages in the document. if ((poems.length % 2) > 0){

function remPages(){
  while(b.pageCount() > 1){

function savePage(i){

  if (i > 9)
    b.savePDF(i + "-nerual.pdf");
    b.savePDF('0' + i + "-nerual.pdf");

function saveSamplePage(){

function setup(){
  b.clear (b.doc());
  // var numPages = 1;
  // for( var i=0; i < numPages; i++) {
    // makePage(i);
    // savePage(i);
  // }  

// This makes it all happen: