Ethan Printz

WTE Bot

Screen Shot 2019-12-13 at 10.12.58 PM.png

There are two primary components of this project: a novel charRNN model trained on a selection of Tisch freshman essay and a polished interface that can be used for any charRNN model.

First, we’ll start with the generation of the model. I manually compiled the 2007-2019 NYU Tisch Mercer Street essays (an annual publication of the best freshman writing) into a single text file. I would like to have written a script to do it automatically, but the formatting changed so much between the years as to make a manually running find and replace commands the only option. The text file worked out to be around 2 MB, which would optimally be large but nonetheless contains enough information for a project that’s mostly intended to be a humorous jest at the repetitive themes and language artsy freshman demonstrate. The model was then trained in Spell on the class’ credits using the given Python charRNN script.

The interface is designed to provide the smoothest possible user experience. I found the demonstrational p5 interface for charRNN had a lot of rough edges. You’d end up confused, clicking buttons when you didn’t need to, and couldn’t combine multiple text generations into one document.

The first improvement I made were taking the raw numerical input boxes and turning them into preset buttons to make the experience more accessible and user friendly. By giving the user only three defined values with textual and color-coded information about the use, it is less likely for the user to feel overwhelmed. I modified the microcopy as well: ‘Autocomplete’ and ‘Randomness’ are understandable even if you are not familiar with machine learning, whereas calling it ‘model’, ‘seed’, and ‘temperature’ is overly technical.
The code for the buttons was a bit more complicated than expected, as they are styled and written from scratch:

$(".selectOption").click(function(){
  // If there other elements selected
  if($(this).parent().find(".selectedOption").not(this).length){
    // Toggle CSS class
    $(this).parent().find(".selectedOption").not(this).removeClass("selectedOption");
    $(this).toggleClass("selectedOption");
    // Toggle image color - checking if it is currently color or white
    if($(this).find("img").attr("src").search("color") == -1){
      // Updated URL string for clicked element
      var updatedURL = $(this).find("img").attr("src").replace("white","color");
    } else{
      // Reset other elements' CSS
      $(this).parent().find("img").each(function (){
        $(this).attr("src", $(this).attr("src").replace("white","color"));
      });
      // Updated URL string for clicked element
      var updatedURL = $(this).find("img").attr("src").replace("color","white");
    }
    // Update Image Color
    $(this).find("img").attr("src",updatedURL);
  }
});
```css
.selectOption{
            display: inline-block;
            box-sizing: border-box;
            background-color: rgb(248, 248, 248);
            margin: 0.6vh;
            padding: 0.3vh;
            border-radius: 0.6vh;
            box-shadow: 0 3px 6px rgba(0,0,0,0.03), 0 3px 6px rgba(0,0,0,0.05);
            cursor: pointer;
            transition: 0.2s;
            user-select: none;
        }
            .selectOption:not(.selectedOption):hover{
                box-shadow: 0 5px 7px rgba(0,0,0,0.06), 0 5px 7px rgba(0,0,0,0.07);
            }
            #lengthLine{color: #607D8B}
            #lengthParagraph{color: #455A64}
            #lengthPage{color: #37474F}
            #tempLow{color: #FFA726}
            #tempModerate{color: #FF7043}
            #tempExtreme{color: #EF5350}
            .selectedOption{
                color: rgb(248, 248, 248)!important;
                box-shadow: 0 5px 7px rgba(0,0,0,0.09), 0 5px 7px rgba(0,0,0,0.11);
            }
                .selectedOption#lengthLine{background-color: #607D8B}
                .selectedOption#lengthParagraph{background-color: #455A64}
                .selectedOption#lengthPage{background-color: #37474F}
                .selectedOption#tempLow{background-color: #FFA726}
                .selectedOption#tempModerate{background-color: #FF7043}
                .selectedOption#tempExtreme{background-color: #EF5350}
            .selectTitle{
                margin-bottom: 0.4vh;
            }
            .optionTitle{
                float: left;
                margin: 0.1vh 0.4vh 0 0.4vh;
            }
            .icon{
                width: 2.3vh;
                height: 2.3 vh;
                border-radius: 2px;
                box-sizing: border-box;
                float: left;
            }

The second major component of the interface is the text input, which has been reprogrammed to be more intuitive to use. You start with a placeholder prompt that disappear when you click into the box, then can type until you’re satisfied with the seed text. You can then either hit the enter key or the arrow next to the end of the text to generate the new text with the given button parameters. Here’s the code for generation:

// Generate new text
function generate() {
  // Prevent starting inference if instance already started
  if(!runningInference) {
    runningInference = true;

    // Get the input text
    var input = $("#newInput").html().toLowerCase();

    // Clear previous input
    $("#newInput").remove();
    // Append HTML of input text
    $("#pageSheet").append(`<span class="oldInput">${input}</span><div id="loadingIcon"></div>`);

    // Check if there's something to send
    if (input.length > 0) {
      // This is what the LSTM generator needs
      // Seed text, temperature, length to outputs
      let data = {
        seed: input,
        temperature: getButtonValue('temp'),
        length: getButtonValue('length')
      };

      // Generate text with the charRNN
      charRNN.generate(data, gotData);
      
      // Upon getting results of model
      function gotData(err, result) {
        // Calculate styling of output text
        let style = `text-decoration-color:${
          colorValues[$(`#tempSelect .selectedOption`).attr("id")]
        };color:${
          colorValues[$(`#lengthSelect .selectedOption`).attr("id")]
        }`;
        // Remove loading icon
        $("#loadingIcon").remove();
        // Append HTML of output text
        $("#pageSheet").append(`<span class="generatedInput" style="${style}">${result.sample}</span>`);
        $("#pageSheet").append(`<div id="newInput" placeholder="Type starter here..." contenteditable="true"></div>`);
        $("#newInput").on("click", generate);
        // Update state variables
        runningInference = false;
      }

      // Get value of inputs
      // inputType can be 'length' or 'temp'
      function getButtonValue(inputType){
        let selectedOption = $(`#${inputType}Select .selectedOption`).attr("id");
        return buttonValues[selectedOption];
      }
    }
  }
}