Face2Face

Taylor Kovacevic, Sofia Curbelo

Inspired by the underground world of electronic music heading back to Detroit, mixed with the clean and colourful aesthetic, we created an interactive experience between typeface and sound.

A performance influenced by the practice of face-2-face djing, we sought to bring the world of underground music to the Rueder font by creating consoles to interact with the words on screen. The digital showcase mirrors the performance and offers varied examples for the font in both size and colour.

GitHub repository

face2face

Concept

We sought to combine music with the font showcase by creating DJ consoles out of plexiglass, that when equipped with tags, would interact with the typeface as well as the sound created for this project. The fonts chosen for this project are mainly Rueder and Helvetica. Rueder was chosen as the main font to showcase, as it works very well in bigger size, while Helvetica serves to add to the design with its ability to work as the fine print.

Control System

Visual Materials

Face2Face used a self-made table of ceramic tiles on a 60×60 plexiglass board with two dj consoles that were laser-cut in the colours blue and green to give it a retro look and finish. While filming and our live performance, we used two light-boxes and four different lights coming from each angle.

laser1

Challenges

The main challenges were related to the abstraction of our original idea and its implementation with the code. The original concept was literal, in using actual musical equipment that would play sounds. This first draft was expanded upon with the idea of not using equipment but rather abstracting through shapes and colours to create a more interesting concept and visual identity.

The main problem with the code was related to the complexity of the interactions between multiple variables, as well as the music. Seemingly, the code would just not be implemented properly. After a good few hours of debugging, alongside help from lecturers, we managed to iron out the code not implementing, though certain issues with values according to the sound and certain variables are still present when testing.

Performance

Code Process

class AudioVisualizer {
  constructor(audioContext, processFrame, processError) {
    this.audioContext = audioContext;
    this.processFrame = processFrame;
    this.connectStream = this.connectStream.bind(this);
    navigator.mediaDevices.getUserMedia({ audio: true, video: false }).
    then(this.connectStream).
    catch(error => {
      if (processError) {
        processError(error);
      }
    });
  }

  connectStream(stream) {
    this.analyser = this.audioContext.createAnalyser();
    const source = this.audioContext.createMediaStreamSource(stream);
    source.connect(this.analyser);
    this.analyser.smoothingTimeConstant = 0.5;
    this.analyser.fftSize = 32;

    this.initRenderLoop(this.analyser);
  }

  initRenderLoop() {
    const frequencyData = new Uint8Array(this.analyser.frequencyBinCount);
    const processFrame = this.processFrame || (() => {});

    const renderFrame = () => {
      this.analyser.getByteFrequencyData(frequencyData);
      processFrame(frequencyData);

      requestAnimationFrame(renderFrame);
    };
    requestAnimationFrame(renderFrame);
  }}


const visualMainElement = document.querySelector('main');
const visualValueCount = 32;
let visualElements;
const createDOMElements = () => {
  let i;
  for (i = 0; i < visualValueCount; ++i) {
    const elm = document.createElement('div');
    visualMainElement.appendChild(elm);
  }

  visualElements = document.querySelectorAll('main div');
};
createDOMElements();


const init = () => {
  // Creating initial DOM elements
  const audioContext = new AudioContext();
  const initDOM = () => {
    visualMainElement.innerHTML = '';
    createDOMElements();
  };
  initDOM();

  // Swapping values around for a better visual effect
  const dataMap = { 0: 15, 1: 10, 2: 8, 3: 9, 4: 6, 5: 5, 6: 2, 7: 1, 8: 0, 9: 4, 10: 3, 11: 7, 12: 11, 13: 12, 14: 13, 15: 14, 16: 1, 17:2, 18:3, 19:10, 20:3, 21:10, 22:1, 23:6, 24:10, 25:1, 26:13, 27:12, 28:1, 29:10, 30:11, 31:5, 32:12};
  const processFrame = data => {
    const values = Object.values(data);
    let i;
    for (i = 0; i < visualValueCount; ++i) {
      const value = values[dataMap[i]] / 255;
      const elmStyles = visualElements[i].style;
      elmStyles.transform = ⁠ scaleX( ${value} ) ⁠;
      elmStyles.opacity = Math.max(.55, value);
      
    }
  };

  const processError = () => {
    visualMainElement.classList.add('error');
    visualMainElement.innerText = 'Please allow access to your microphone in order to see this demo.\nNothing bad is going to happen... hopefully :P';
  };

  const a = new AudioVisualizer(audioContext, processFrame, processError);
};