<h1>REAPER SWS Loudness Visualizer</h1>
<h2>Instructions</h2>
<p>Copy the following code in your SWS Loudness options export format:</p>
<pre><code>File: $target, Integrated: $integrated, Range: $range, True peak: $truepeak, Max ST: $maxshort, Max M: $maxmomentary</code></pre>
<p>More info on the <a href="https://forum.cockos.com/showthread.php?p=2074179">Cockos Forum Thread</a>.</p>
<h2>Input</h2>
<textarea id="textinput">
File: Alien2347-E01.mp3, Integrated: -13.8 LUFS, Range: 7.7 LU, True peak: 0.8 dBTP, Max ST: -5.3 LUFS, Max M: -2.8 LUFS
File: Alien2347-E02.mp3, Integrated: -14.6 LUFS, Range: 6.2 LU, True peak: 0.1 dBTP, Max ST: -9.4 LUFS, Max M: -7.6 LUFS
File: Alien2347-E03.mp3, Integrated: -13.9 LUFS, Range: 5.3 LU, True peak: 0.4 dBTP, Max ST: -8.8 LUFS, Max M: -7.1 LUFS
File: Alien2347-E04.mp3, Integrated: -12.1 LUFS, Range: 4.9 LU, True peak: 0.3 dBTP, Max ST: -4.6 LUFS, Max M: -3.3 LUFS
File: Alien2347-E05.mp3, Integrated: -13.1 LUFS, Range: 5.0 LU, True peak: 0.8 dBTP, Max ST: -8.8 LUFS, Max M: -6.4 LUFS
File: Alien2347-E06.mp3, Integrated: -13.5 LUFS, Range: 5.8 LU, True peak: 0.4 dBTP, Max ST: -7.8 LUFS, Max M: -4.9 LUFS
File: Alien2347-E07.mp3, Integrated: -11.4 LUFS, Range: 5.7 LU, True peak: 0.4 dBTP, Max ST: -7.5 LUFS, Max M: -5.4 LUFS
File: Alien2347-E08.mp3, Integrated: -12.1 LUFS, Range: 4.5 LU, True peak: 0.8 dBTP, Max ST: -8.5 LUFS, Max M: -4.5 LUFS
File: Alien2347-E09.mp3, Integrated: -11.4 LUFS, Range: 4.8 LU, True peak: 0.3 dBTP, Max ST: -7.2 LUFS, Max M: -4.3 LUFS
File: Alien2347-E10.mp3, Integrated: -13.7 LUFS, Range: 5.6 LU, True peak: 0.2 dBTP, Max ST: -9.8 LUFS, Max M: -6.3 LUFS
File: Alien2347-E11.mp3, Integrated: -12.4 LUFS, Range: 5.0 LU, True peak: 0.1 dBTP, Max ST: -7.4 LUFS, Max M: -4.9 LUFS
File: Alien2347-E12.mp3, Integrated: -12.3 LUFS, Range: 4.6 LU, True peak: 0.3 dBTP, Max ST: -8.0 LUFS, Max M: -4.9 LUFS
File: Alien2347-E13.mp3, Integrated: -12.8 LUFS, Range: 6.5 LU, True peak: 0.5 dBTP, Max ST: -7.2 LUFS, Max M: -5.3 LUFS
File: Alien2347-E14.mp3, Integrated: -13.0 LUFS, Range: 4.9 LU, True peak: 0.3 dBTP, Max ST: -8.0 LUFS, Max M: -3.6 LUFS
File: Alien2347-E15.mp3, Integrated: -12.5 LUFS, Range: 5.9 LU, True peak: 0.6 dBTP, Max ST: -6.9 LUFS, Max M: -3.7 LUFS
File: Alien2347-E16.mp3, Integrated: -12.9 LUFS, Range: 4.7 LU, True peak: 0.7 dBTP, Max ST: -6.7 LUFS, Max M: -3.9 LUFS
File: Alien2347-E17.mp3, Integrated: -12.4 LUFS, Range: 5.0 LU, True peak: 0.4 dBTP, Max ST: -6.3 LUFS, Max M: -4.4 LUFS
File: Alien2347-E18.mp3, Integrated: -11.3 LUFS, Range: 5.0 LU, True peak: 0.5 dBTP, Max ST: -7.2 LUFS, Max M: -2.8 LUFS
File: Alien2347-E19.mp3, Integrated: -12.3 LUFS, Range: 5.3 LU, True peak: 0.6 dBTP, Max ST: -7.4 LUFS, Max M: -5.0 LUFS
File: Alien2347-E20.mp3, Integrated: -11.5 LUFS, Range: 5.1 LU, True peak: 0.2 dBTP, Max ST: -5.3 LUFS, Max M: -3.6 LUFS
File: Alien2347-E21.mp3, Integrated: -11.4 LUFS, Range: 5.9 LU, True peak: 0.2 dBTP, Max ST: -5.7 LUFS, Max M: -1.8 LUFS
</textarea>

<button id="button_process">Process</button>

<h2 id="output">Output</h2>
<div id="container"></div>
* {
  box-sizing: border-box;
}

body {
  background: #333333;
  color: white;
  font-family: Helvetica, sans-serif;
}

pre {
  padding: 1em;
  background: #AA6600;
  border: 0;
  width: 100%;
}

#textinput {
  display: block;
  text-align:center;
  padding: 2em;
  width: 100%;
  height: 20vw;
  margin:auto;
  border: 0;
  margin-bottom: 1vw;
  background: #444444;
  color: white;
}

#button_process {
  display:block;
  margin:auto;
  width: 50%;
  padding: 0;
  border: 0;
  height: 2em;
  margin-bottom: 3vw;
}

.highcharts-boxplot-median {
  stroke: grey;
}

.highcharts-boxplot-box {
  stroke: grey;
}

a {
  color: #AA6600;
}

#output {
  display:none;
}

pre {
  white-space: pre-wrap;
}
var y_min = -30;
var y_max = 3;

var min_range = 10;
var scale = chroma.scale(['red', 'red', 'red', 'orange', 'yellow', 'lawngreen', 'cyan']).mode('lab') // Create a color palette, inverted

var button = document.getElementById("button_process");
var k = ["file", "integrated", "range", "truepeak"];

button.addEventListener("click", Process, false);

function Process(e) {
  var title_output = document.getElementById("output");
  title_output.style.display = "block";
  var text_area = document.getElementById("textinput");
  var text = text_area.value;
  var lines = text.split("\n");
  
  data = [];
  lines.forEach(function(line, i) {
    var columns = line.split(",");
    if (columns[1] !== undefined) {
      data[i] = {};
      columns.forEach(function(column, j) {
        var value = column.split(":");
        if (value[1] !== undefined && k[j] !== undefined) {
          var number = parseFloat(value[1].replace(" LUFS", ""));
          if (!isNaN(number)) {
            data[i][k[j]] = number;
          } else {
            data[i][k[j]] = value[1];
          }
        }
      });
    }
  });
  console.log("data", data);
  
  var names = data.map(x => x.file);
  console.log("names", names);
  
  var series = [{ data: [] }];
  var average_q1 = 0
  var average_median = 0
  var average_q3 = 0
  var max_high = 0
  data.forEach(function(d) {
    var coef =  d.range / min_range
    series[0].data.push({
      low: y_min,
      q1: d.integrated - d.range / 2,
      median: d.integrated,
      q3: d.integrated + d.range / 2,
      high: d.truepeak,
      fillColor: scale( coef ).hex()
    });
    average_q1 = average_q1 + d.integrated - d.range / 2
    average_q3 = average_q3 + d.integrated + d.range / 2
    average_median = average_median + d.integrated
    max_high = max_high + d.truepeak
  });
  if( series[0].data.length > 1 ) {
    names.push("Averages");
    series[0].data.push({
      low: y_min,
      q1: Math.floor(average_q1 / series[0].data.length*100)/100,
      median: Math.floor(average_median / series[0].data.length*100)/100,
      q3: Math.floor(average_q3 / series[0].data.length*100)/100,
      high: Math.floor(max_high / series[0].data.length*100)/100,
      fillColor: "#a8d5ff"
    });
  }
  console.log("series", series);
  DrawChart(series, names);
}

function DrawChart(series, names) {
  series[0].tooltip = {
    pointFormatter: function() {
      var str =
        "<strong>Integrated</strong>: " +
        this.median +
        "<br><strong>Range</strong>: " +
        Math.round((this.q3 - this.q1) * 100) / 100 +
        "<br><strong>Low Range</strong>: " +
        Math.round(this.q1 * 100) / 100 +
        "<br><strong>High Range</strong>: " +
        Math.round(this.q3 * 100) / 100 +
        "<br><strong>True peak</strong>: " +
        this.high;
      return str;
    }
  };

  Highcharts.chart("container", {
    title: {
      text: "Loudness Chart"
    },

    chart: {
      type: "boxplot",
      height: 720
    },
    
    exporting: {
      sourceWidth: 1920,
      sourceHeight: 1080
    },

    credits: {
      enabled: false
    },

    legend: {
      enabled: false
    },

    xAxis: {
      categories: names
    },

    yAxis: [
      {
        title: {
          text: "LUFS"
        },
        crosshair: {
          snap: false,
          zIndex: 3,
          color: "#000000"
        },
        min: y_min,
        max: y_max,
        tickInterval: 3,
        minorTickInterval: 1,
        plotLines: [
          {
            value: 0,
            color: "red",
            width: 2
          },
          {
            value: -23,
            color: "green",
            width: 2
          }
        ]
      },
      {
        opposite: true,
        title: {
          text: "LUFS"
        },
        crosshair: {
          snap: false,
          zIndex: 3,
          color: "#000000"
        },
        min: y_min,
        max: y_max,
        tickInterval: 3,
        minorTickInterval: 1,
        plotLines: [
          {
            value: 0,
            color: "red",
            width: 2
          },
          {
            value: -23,
            color: "green",
            width: 2
          }
        ]
      }
    ],
    //
    series: series
  });
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://code.highcharts.com/highcharts.js
  2. https://code.highcharts.com/highcharts-more.js
  3. https://code.highcharts.com/modules/exporting.js
  4. https://cdnjs.cloudflare.com/ajax/libs/chroma-js/2.0.3/chroma.min.js