diff --git a/uhepp-js/src/components/UheppHist.jsx b/uhepp-js/src/components/UheppHist.jsx
index d15e1c04bb05d6c67a2f81ad3698feb36a424836..b4df53b06ac28e03f45391585c756eed3b4b2024 100644
--- a/uhepp-js/src/components/UheppHist.jsx
+++ b/uhepp-js/src/components/UheppHist.jsx
@@ -559,6 +559,22 @@ const Legend = ({
 }
 
 const UheppHist = ({width, height, uhepp}) => {
+  const post_uhepp = Object.assign({}, uhepp, {
+      yields: preprocessData({
+        yields: uhepp.yields,
+        old_edges: uhepp.bins.edges,
+        new_edges: uhepp.bins.rebin, 
+        include_underflow: uhepp.bins.include_underflow, 
+        include_overflow: uhepp.bins.include_overflow, 
+      })
+  })
+
+
+  return <UheppHistPost width={width} height={height} uhepp={post_uhepp} />
+}
+
+const UheppHistPost = ({width, height, uhepp}) => {
+  const post_uhepp = uhepp  // backwards compatibility
 
   const [highlightedBin, setHighlightedBin] = useState(null);
   const [showTotal, setShowTotal] = useState(false);
@@ -577,15 +593,6 @@ const UheppHist = ({width, height, uhepp}) => {
     "#1f77b4",
   ])
 
-  const post_uhepp = Object.assign({}, uhepp, {
-      yields: preprocessData({
-        yields: uhepp.yields,
-        old_edges: uhepp.bins.edges,
-        new_edges: uhepp.bins.rebin, 
-        include_underflow: uhepp.bins.include_underflow, 
-        include_overflow: uhepp.bins.include_overflow, 
-      })
-  })
 
 	const margin = {
 		top: 10,
diff --git a/uhepp-js/src/components/UheppHistUI.jsx b/uhepp-js/src/components/UheppHistUI.jsx
index 333c00ec51b0f04e98f7bb8788ba4de1c17c8a82..defa1bdb2bb6c41998390653319050f9b43bd994 100644
--- a/uhepp-js/src/components/UheppHistUI.jsx
+++ b/uhepp-js/src/components/UheppHistUI.jsx
@@ -1,6 +1,37 @@
 import React, { useState } from "react";
 import UheppHist from "./UheppHist.jsx";
 
+const noVariationUsed = (uhepp) => {
+  const main_names = uhepp.stacks.map(stack => stack.content.map(item => item.yields)).flat()
+  const num_names = uhepp.ratio.map(item => item.numerator).flat()
+  const den_names = uhepp.ratio.map(item => item.denominator).flat()
+  const names = [main_names, den_names, num_names].flat()
+    
+  const varied_names = names.filter(name => (name && name.indexOf("/") != -1))
+  return varied_names.length == 0
+}
+
+const variationList = (uhepp) => {
+  return Array.from(new Set(Object.entries(uhepp.yields).map(([key, item]) => [
+      ...Object.keys(item.var_up || {}),
+      ...Object.keys(item.var_down || {})
+  ]).flat())).sort()
+}
+
+const makeVariationStacks = (uhepp, variation, updown) => {
+  return  uhepp.stacks.map(stack => 
+          ({
+            type: "step", 
+            content: [{
+              "label": updown,
+              "yield": stack.content.map(item => 
+                item.yield.map(name => `${name}/${variation}/${updown}`)
+              ).flat()
+            }]
+          })
+   )
+}
+
 const UheppHistUI = ({width, height, uhepp}) => {
   const [uhepp_data, setData] = useState(uhepp)
 
@@ -30,7 +61,22 @@ const UheppHistUI = ({width, height, uhepp}) => {
     ))
   }
 
-  let rebin = uhepp_data.bins.rebin || uhepp_data.bins.edges
+  const handleEnvelop = (e) => {
+    let variationName = e.target.value
+    setData(Object.assign({}, uhepp_data,
+      {stacks: [
+        ...uhepp.stacks,
+        ...makeVariationStacks(uhepp, variationName, "up"),
+        ...makeVariationStacks(uhepp, variationName, "down"),
+      ]}
+    ))
+
+  }
+
+  const variations = variationList(uhepp)
+  const isVariationReady = noVariationUsed(uhepp) && (variations.length > 0)
+
+  const rebin = uhepp_data.bins.rebin || uhepp_data.bins.edges
   return <>
 		<div>{ Object.entries(uhepp.metadata.tags).map(([key,  value]) => 
 				value == null ?
@@ -49,9 +95,9 @@ const UheppHistUI = ({width, height, uhepp}) => {
 			<li className="nav-item">
 				<a className="nav-link" id="binning-tab" data-toggle="tab" href="#binning" role="tab" aria-controls="binning" aria-selected="true">Binning</a>
 			</li>
-			{ /*<li className="nav-item">
-				<a className="nav-link" id="other-tab" data-toggle="tab" href="#other" role="tab" aria-controls="other" aria-selected="false">Other</a>
-			</li> */}
+			{ <li className="nav-item">
+				<a className="nav-link" id="variations-tab" data-toggle="tab" href="#variations" role="tab" aria-controls="variations" aria-selected="false">Variations</a>
+			</li> }
 			<li className="nav-item">
 				<a className="nav-link" id="reset-tab" data-toggle="tab" href="#reset" role="tab" aria-controls="reset" aria-selected="false">Reset</a>
 			</li>
@@ -91,9 +137,10 @@ const UheppHistUI = ({width, height, uhepp}) => {
 				<form>
 					<div className="form-group">
 						<label htmlFor="rebin">Bin edges</label>
-						<select multiple size={10} className="form-control" id="rebin" onChange={(e) => handleRebin(e)}>
+						<select multiple size={10} className="form-control" id="rebin" onChange={(e) => handleRebin(e)}
+                defaultValue={rebin}>
 							{uhepp.bins.edges.map((v, i) => 
-								<option value={v} key={i} selected={rebin.indexOf(v) >= 0}>{v}</option>
+								<option value={v} key={i}>{v}</option>
 							)}
 						</select>
 					</div>
@@ -131,9 +178,22 @@ const UheppHistUI = ({width, height, uhepp}) => {
 					</div>
 				</form>
 			</div>
-			{ /*<div className="tab-pane" id="other" role="tabpanel" aria-labelledby="other-tab">
-				<h1>La di da</h1>
-			</div> */}
+			<div className="tab-pane p-3" id="variations" role="tabpanel" aria-labelledby="variations-tab">
+        { !isVariationReady && 
+          <p>To use the variation feature, you must add variation data to
+          the <code>yields</code> object. Please note that the stacks must not use
+          any varied yield in the default view.</p> }
+        { isVariationReady && 
+        <form>
+					<div className="form-group">
+						<label htmlFor="envelop">Envelop</label>
+						<select className="form-control" id="envelop" onChange={(e) => handleEnvelop(e)}>
+              { variations.map((name, i) => <option value={name} key={i}>{name}</option>) }
+            </select>
+          </div>
+        </form>
+        }
+			</div>
 			<div className="tab-pane p-3" id="reset" role="tabpanel" aria-labelledby="reset-tab">
         <p>Reset all view modifications. Pull the plot to make permanent
         changes.</p>
diff --git a/uhepp-js/src/helpers/uhepp.js b/uhepp-js/src/helpers/uhepp.js
index c3a14b3d3fef78779c233d4c1b11d1e6a07306c7..1bd6590498c10ad612df80a9c3ff5c6ef0730493 100644
--- a/uhepp-js/src/helpers/uhepp.js
+++ b/uhepp-js/src/helpers/uhepp.js
@@ -32,15 +32,35 @@ export const aggregate = ({
   bin_index
 }) => {
   const getBase = (process_name) => {
-    if (!yields.hasOwnProperty(process_name)) {
-      return 0
-    }
-    const process = yields[process_name]
-    if (!process.hasOwnProperty(property)) {
-      return 0
+    if (process_name.indexOf("/") != -1) {
+      if (property != "base") {
+        return 0
+      }
+      const [name, variation, updown] = process_name.split("/")
+      if (!yields.hasOwnProperty(name)) {
+        return 0
+      }
+      const process = yields[name]
+      const var_obj = "var_" + updown
+      if (!process.hasOwnProperty(var_obj) || !process[var_obj].hasOwnProperty(variation)) {
+        // Fall back
+        return aggregate({yields, process_names: [name], property, pre, post, bin_index})
+      }
+
+      const base = process[var_obj][variation]
+      return (bin_index != null) ? base[bin_index] : base;
+
+    } else {
+      if (!yields.hasOwnProperty(process_name)) {
+        return 0
+      }
+      const process = yields[process_name]
+      if (!process.hasOwnProperty(property)) {
+        return 0
+      }
+      const base = process[property]
+      return (bin_index != null) ? base[bin_index] : base;
     }
-    const base = process[property]
-    return (bin_index != null) ? base[bin_index] : base;
   }
   const agg = process_names.map(getBase).flat().map(pre).reduce((a, b) => a + b, 0)
   return post(agg)
@@ -153,6 +173,8 @@ const fromEntires = (entries) => {
   return result
 }
 
+export const objmap = (obj, map) => fromEntires(Object.entries(obj).map(([key, value]) => [key, map(value, key)]))
+
 /**
  * Processes the yield objects and returns a new version
  *
@@ -178,6 +200,13 @@ export const preprocessData = ({
   const rebinned = {
     "base": rebin(yield_obj.base, old_edges, eff_edges),
     "stat": yield_obj.stat ? rebin(yield_obj.stat, old_edges, eff_edges, (x) => x*x, (x)=>Math.sqrt(x)) : null,
+    "syst": yield_obj.syst ? rebin(yield_obj.syst, old_edges, eff_edges, (x) => x*x, (x)=>Math.sqrt(x)) : null,
+    "var_up": yield_obj.var_up ? objmap(yield_obj.var_up,
+      (value) => rebin(value, old_edges, eff_edges, (x) => x*x, (x)=>Math.sqrt(x))
+    ) : null,
+    "var_down": yield_obj.var_down ? objmap(yield_obj.var_down,
+      (value) => rebin(value, old_edges, eff_edges, (x) => x*x, (x)=>Math.sqrt(x))
+    ) : null,
   }
   if (include_underflow) {
     rebinned.base[1] += rebinned.base[0]
diff --git a/uhepp-js/tests/helpers.js b/uhepp-js/tests/helpers.js
index acad4fbe57523b6fb889ff62f92a2f5045a95ad2..e85cb3c2054dbc0ce5d3df182409489ccccb106e 100644
--- a/uhepp-js/tests/helpers.js
+++ b/uhepp-js/tests/helpers.js
@@ -1,6 +1,6 @@
 import { use, assert, expect } from 'chai';
 import chaiAlmost from 'chai-almost';
-import { sumBase, sumStat, rebin, histogramify } from "../src/helpers/uhepp.js";
+import { sumBase, sumStat, rebin, histogramify, objmap } from "../src/helpers/uhepp.js";
 
 use(chaiAlmost())
 
@@ -69,6 +69,31 @@ describe("sumBase", () => {
         const sum = sumBase({}, [], 2)
         assert.equal(sum, 0);
     })
+
+    const syst_yield = {
+      "process_a": {
+        "base": [ 0,  1,  2,  3,  4,  5],
+        "stat": [0.1, 0.1, 0.1, 0.1, 0.1],
+        "var_up": {
+          "ele_eff": [1, 2, 3, 4, 5, 6],
+        },
+        "var_down": {
+          "mu_eff": [0, 0, 1, 2, 3, 4],
+        }
+      },
+    }
+    it("should return up variations", () => {
+        const sum = sumBase(syst_yield, ["process_a/ele_eff/up"])
+        assert.equal(sum, 21);
+    })
+    it("should return down variations", () => {
+        const sum = sumBase(syst_yield, ["process_a/mu_eff/down"])
+        assert.equal(sum, 10);
+    })
+    it("should return fall back to base", () => {
+        const sum = sumBase(toy_yield, ["process_a/mu_eff/up"])
+        assert.equal(sum, 15);
+    })
 });
 
 describe("sumStat", () => {
@@ -136,6 +161,22 @@ describe("sumStat", () => {
         const sum = sumStat({}, [], 2)
         assert.equal(sum, 0);
     })
+    const syst_yield = {
+      "process_a": {
+        "base": [ 0,  1,  2,  3,  4,  5],
+        "stat": [0.1, 0.1, 0.1, 0.1, 0.1],
+        "var_up": {
+          "ele_eff": [1, 2, 3, 4, 5, 6],
+        },
+        "var_down": {
+          "mu_eff": [0, 0, 1, 2, 3, 4],
+        }
+      },
+    }
+    it("should fall back to zero for variations", () => {
+        const sum = sumStat(syst_yield, ["process_a/ele_eff/up"])
+        assert.equal(sum, 0);
+    })
 });
 
 describe("rebin", () => {
@@ -207,3 +248,18 @@ describe("histogramify", () => {
     })
 
 })
+
+describe("objmap", () => {
+    it("map values", () => {
+      const obj = {"x": 1, "y": 2}
+      const derived = objmap(obj, value => value*value)
+      assert.equal(derived.x, 1)
+      assert.equal(derived.y, 4)
+    })
+    it("map values based on key", () => {
+      const obj = {"x": 1, "y": 2}
+      const derived = objmap(obj, (value, key) => key)
+      assert.equal(derived.x, "x")
+      assert.equal(derived.y, "y")
+    })
+})