diff --git a/viewer/ts/profile.ts b/viewer/ts/profile.ts
index 661531b..ae1ab76 100644
--- a/viewer/ts/profile.ts
+++ b/viewer/ts/profile.ts
@@ -209,17 +209,23 @@ class Profile {
   private _get_min_points: () => number;
   private _display_warning: (title: string, msg: string) => void;
   private _progress_points: string[] = null;
-  constructor(profile_text: string, container: d3.Selection<any>, legend: d3.Selection<any>, get_min_points: () => number, display_warning: (title: string, msg: string) => void) {
+  constructor(container: d3.Selection<any>, legend: d3.Selection<any>, get_min_points: () => number, display_warning: (title: string, msg: string) => void) {
     this._plot_container = container;
     this._plot_legend = legend;
     this._get_min_points = get_min_points;
     this._display_warning = display_warning;
-    let lines = profile_text.split('\n');
+  }
+
+  public loadFromFileString(profile_text: string) {
+    this.loadFromFileLines(profile_text.split('\n'));
+  }
+
+  public async loadFromFileLines(profile_lines: AsyncIterable<string> | Iterable<string>) {
     let experiment: Experiment = null;

-    for (let i = 0; i < lines.length; i++) {
-      if (lines[i].length == 0) continue;
-      let entry = parseLine(lines[i]);
+    for await (let line of profile_lines) {
+      if (line.length == 0) continue;
+      let entry = parseLine(line);

       if (entry.type === 'startup') {
         // Do nothing
@@ -236,7 +242,7 @@ class Profile {
       } else if (entry.type === 'latency-point') {
         this.addLatencyMeasurement(experiment, entry);
       } else {
-        display_warning('Invalid Profile', 'The profile you loaded contains an invalid line: <pre>' + lines[i] + '</pre>');
+        display_warning('Invalid Profile', 'The profile you loaded contains an invalid line: <pre>' + line + '</pre>');
       }
     }
     
diff --git a/viewer/ts/ui.ts b/viewer/ts/ui.ts
index 93dda57..fa671f7 100644
--- a/viewer/ts/ui.ts
+++ b/viewer/ts/ui.ts
@@ -173,21 +173,38 @@ d3.select('#load-profile-file').on('change', function() {

   d3.select('#load-profile-filename').attr('value', file_browser.value.replace(/C:\\fakepath\\/i, ''));

-  open_button.classed('disabled', false)
-    .on('click', function() {
+  function readFileAsText(file: Blob) {
+    return new Promise<string>((resolve, reject) => {
       var reader = new FileReader();
-      reader.onload = function(event) {
-        let contents: string = (<any> event.target).result;
-        current_profile = new Profile(contents, d3.select('#plot-area'), d3.select('#legend'), get_min_points, display_warning);
-        update();
-      };

       reader.onerror = function(event) {
-        console.error("Unable to read file. Error code: " + (<any> event.target).error.code);
+        reject("Unable to read file. Error code: " + (<any> event.target).error.code);
+      };
+      reader.onload = function(event) {
+        let contents: string = (<any> event.target).result;
+        resolve(contents);
       };

-      // Read the profile
-      reader.readAsText(file_browser.files[0]);
+      reader.readAsText(file);
+    });
+  }
+  async function* readFileAsLines(file: Blob) {
+    const CHUNK_SIZE = 64 * 1024 * 1024;
+    let buffer = "";
+    for (let i = 0; i < file.size; i += CHUNK_SIZE) {
+      console.log(`Loading file ${i} / ${file.size}`);
+      buffer += await readFileAsText(file.slice(i, i + CHUNK_SIZE));
+      let lines = buffer.split('\n');
+      buffer = lines.pop();
+      yield* lines;
+    }
+    yield buffer;
+  }
+  open_button.classed('disabled', false)
+    .on('click', async function() {
+      current_profile = new Profile(d3.select('#plot-area'), d3.select('#legend'), get_min_points, display_warning);
+      await current_profile.loadFromFileLines(readFileAsLines(file_browser.files[0]));
+      update();

       // Clear the file browser value
       file_browser.value = '';
@@ -222,7 +239,8 @@ let samples_sel = d3.select('#samples').selectAll('.sample-profile').data(sample
         xhr.open('GET', `profiles/${d}.coz`);
         xhr.onload = function() {
           current_profile = sample_profile_objects[d] =
-            new Profile(xhr.responseText, d3.select('#plot-area'), d3.select('#legend'), get_min_points, display_warning);
+            new Profile(d3.select('#plot-area'), d3.select('#legend'), get_min_points, display_warning);
+          current_profile.loadFromFileString(xhr.responseText);
           update();
         };
         xhr.onerror = function() {
diff --git a/viewer/tsconfig.json b/viewer/tsconfig.json
index ba2a73d..61184dd 100644
--- a/viewer/tsconfig.json
+++ b/viewer/tsconfig.json
@@ -2,6 +2,10 @@
     "compilerOptions": {
         "module": "none",
         "target": "es5",
+        "lib": [
+            "esnext",
+            "dom"
+        ],
         "noImplicitAny": true,
         "sourceMap": true
     }
