Web Terminal

Updated on June 26th, 2021 at 1:42 am

What is it?

The Web Terminal was an experiment that I attempted after seeing an example of a terminal-like interface online. I reversed engineered the page that I found by examining the source code. I then pieced together the styles of the page until I got something that looked similar. The end result was spectacular. The site looks and feels like a 1960s dumb terminal that you would find at a computer lab at MIT.

screenshot of web terminal website
Web Terminal Interface

What does it do?

At the time of this writing, the Web Terminal does not do very much at all. It became more of a prototype than an actual project after I discovered that the limitations of Javascript put a roadblock on what I could do. I had plans of making it into a sort of story-based game engine that was controlled by a command-line interface (very similar to DOS/UNIX based adventure games), but the linear scripting style and lack of object-oriented data structures limited this from happening. I thought of rebuilding the terminal with PHP, but the fact that PHP requires page refreshes to execute functionality made the whole concept of a terminal moot.

Looking back on it many years later, it may be possible to expand the capabilities of the terminal by incorporating it into a JavaScript Framework. Using a framework like React or Angular may be exactly what the terminal needs to actually work. I would then need a source of persistent data to store user-created scripts. This would mean tieing the framework to a server-side language so that it can interact with a database. Since the terminal is written in JavaScript, using Node and Express may be the best option.

The only problem I see with using a server-side language is that (like my attempt with PHP) the page requires a refresh in order to save data. Or, at least it requires a refresh using the REST API. This would mean that I would need to save the state of the terminal before executing a REST operation, or find a way to interact with the data without refreshing the page.

How does it work?

The CSS

What makes the Terminal interesting is probably its CSS. There are surprisingly fewer selectors and properties than you would expect. The background is made up of a radial gradient and a very small image of a pixel that repeats across the top layer. This gives the interface a retro screen look. Additionally, there is a flicker animation that dims and brightens the screen over and over to give a CRT screen effect.

@-webkit-keyframes flicker {
  0% { opacity: 0.75; }
  10% { opacity: 0.7; }
  20% { opacity: 0.65; }
  30% { opacity: 0.7; }
  40% { opacity: 0.75; }
  50% { opacity: 0.65; }
  60% { opacity: 0.75; }
  70% { opacity: 0.72; }
  80% { opacity: 0.65; }
  90% { opacity: 0.72; }
  100% { opacity: 0.7; }
}
.flicker {
  -webkit-animation-name: flicker;
  -webkit-animation-duration: 1s;
  -webkit-animation-iteration-count: infinite;
  -webkit-animation-timing-function: ease-in-out;
}

body {
	background: -webkit-radial-gradient(center center, contain, rgba(0,150,0,1), black) center center no-repeat, black;	
  -webkit-background-size: 110% 100%;
  	height: 100%;
	font-family: Inconsolata, monospace;
	font-size: 14px;
	text-shadow: 0 0 5px #C8C8C8;
	color: #FFF;
}
#screen {
	position: fixed;
	width: 100%;
	height: 100%;
	top: 0;
	left: 0;
	background: url("http://www.htmlfivewow.com/demos/terminal/interlace.png") top left repeat, rgba(255,255,255,0.5);
	opacity: 0.2;
    z-index: 10;
}

The JavaScript

I was really a fan of the MVC (Model, View, Controller) style of programming where you separate the data from the logic and the logic from the interface. Though I never really practiced coding in that format at the time, I still wanted to design the files of the terminal around that concept. I have two main logic files: terminal.js and controller.js. terminal.js is responsible for building the elements on the screen such as the $ symbol and the text field after it. It is also responsible for creating the next text field after each command is entered and then removing the previous one. This creates the effect of a changing command line. Below is the code for how I did it.

textField.addEventListener("keydown", function(e) {

  // Run the following scripts if RETURN is clicked
  if (e.keyCode == 13) {

  // Append textInput
  var textInput = document.createTextNode(textField.value);
  contentArea.appendChild(textInput);

  // Remove existing text field
  textField.parentNode.removeChild(textField);

  //Increment the line
  var lineBreak = document.createElement("br");
  contentArea.appendChild(lineBreak);	

  // Submit the input to the event function
  processFunction(textInput);
  }
}

The terminal.js file is also responsible for making the keyboard clicking sounds when you type. I created this effect by finding a YouTube video of someone typing on an Apple 2 keyboard. Then I recorded an audio sample, imported it into Adobe Audition, cropped the timeline to 5 unique sounding key strokes, and exported each one as an individual WAV file. I then called a random function to play one of the five audio files every time the keydown event listener was invoked. It’s a real neat idea, though there is kind of a delay in some browsers which ruins the effect a bit.

The controller.js file is responsible for providing a bit of functionality to the program. I said before that the terminal didn’t do very much, and it still doesn’t, but it does have a few commands to show that it has potential. The idea behind the terminal was to create text games, so one of the few commands it does have is to list and select available sessions from a list. You can do this by typing list sessions and load session 1.

// Function to list all of the sessions in the sessionsArray
function listSessions() {
  writeToScreen("List of Current Sessions", "orange");
  lineBreak();
  var sessionCount = 0;
  for (var i = 0; i < sessionsArray.length; i++) {
    writeToScreen(sessionsArray[i].sessionName +
      " (" +
      sessionsArray[i].sessionVersion +
      ") " +
      sessionsArray[i].sessionAuthor, "list");
    sessionCount++;
  }
  lineBreak();
  writeToScreen("Total Found: " + sessionCount);
}

// Function that controlls the start of the program
function start() {
  sessionInit();
  writeToScreen("Web Terminal (" + terminalVersion + ")");
  writeToScreen("By Ryan Bains-Jordan");
  newCommand(sessionChoice);
}

function sessionChoice(argument) {

  var choice = argument.textContent;

  switch (choice) {
    case "load session 1":
      writeToScreen("Starting Session 1...");
      startSession1();
      break;
    case "load session 2":
      writeToScreen("Starting Session 2...");
      break;
    case "list sessions":
      listSessions();
      newCommand(sessionChoice);
      break;
    default:
      writeToScreen(choice + ": command not found.");
      newCommand(sessionChoice);
  }
}

Some of the functions seen above like writeToScreen and session are from a file called funclib.js. This was designed to be a function library that made it easier to interact with the terminal. writeToScreen for example, did all of the work to create the text nodes, style them according to a parameter, and then append them to the screen. The newCommand function actually came from terminal.js though. In fact, the entire purpose of the file is specifically for that function as it creates the text nodes/fields and listens for a key input.

Links