diff --git a/uhepp-js/src/components/Graph.jsx b/uhepp-js/src/components/Graph.jsx
index f9062e2e3004b948ce4e7fad136cb48ebb0ce147..98c058e2ae86120d0cd6812dc1185df57ed43a6d 100644
--- a/uhepp-js/src/components/Graph.jsx
+++ b/uhepp-js/src/components/Graph.jsx
@@ -11,6 +11,7 @@ import { SVG } from 'mathjax-full/js/output/svg';
 import { browserAdaptor } from 'mathjax-full/js/adaptors/browserAdaptor';
 import { RegisterHTMLHandler } from 'mathjax-full/js/handlers/html';
 import { STATE } from 'mathjax-full/js/core/MathItem';
+import { preprocessData, sumBase, sumStat } from "../helpers/uhepp.js";
 
 const getMaxBin =(uhepp) => {
   let max = 0;
@@ -19,10 +20,8 @@ const getMaxBin =(uhepp) => {
     uhepp.stacks.forEach((stack, stack_index) => {
       let bottom = 0
       stack.content.forEach((stack_item, si_i) => {
-        const y_value = stack_item["yield"].map(name => uhepp.yields[name].base[bin_i + 1]).reduce((a, b) => a + b)
-        if (y_value > 0) {
-          bottom += y_value;
-        }
+        const y_value = sumBase(uhepp.yields, stack_item["yield"], bin_i)
+        if (y_value > 0) { bottom += y_value; }
       })
       max = Math.max(max, bottom)
     })
@@ -42,19 +41,20 @@ const Histogram = ({
   children
 }) => {
 
-  const n_bins = uhepp.bins.edges.length - 1
+  const edges = uhepp.bins.rebin || uhepp.bins.edges
+  const n_bins = edges.length
   let objects = []
 
-  for (let bin_i=0; bin_i < n_bins; bin_i++) {
+  for (let bin_i=1; bin_i < n_bins; bin_i++) {
     uhepp.stacks.forEach((stack, stack_index) => {
       let bottom = 0
       stack.content.forEach((stack_item, si_i) => {
-        const y_value = stack_item["yield"].map(name => uhepp.yields[name].base[bin_i + 1]).reduce((a, b) => a + b)
+        const y_value = sumBase(uhepp.yields, stack_item["yield"], bin_i)
         if (stack.type == "stepfilled") {
           objects.push(<rect
             key={`rect-${stack_index}-${si_i}-${bin_i}`}
-            x={xScale(uhepp.bins.edges[bin_i])}
-            width={xScale(uhepp.bins.edges[bin_i + 1]) - xScale(uhepp.bins.edges[bin_i])}
+            x={xScale(edges[bin_i-1])}
+            width={xScale(edges[bin_i + 1]) - xScale(edges[bin_i])}
             y={yScale(bottom + y_value)}
             height={yScale(bottom) - yScale(bottom + y_value)}
             fill={stack_item.style ? stack_item.style.color : '#1f77b4'}
@@ -62,12 +62,12 @@ const Histogram = ({
             onMouseOver={() => onMouseOverBin(bin_i)}
             />)
         } else if (stack.type == "points") {
-          const width = xScale(uhepp.bins.edges[bin_i + 1]) - xScale(uhepp.bins.edges[bin_i])
-          const stat2 = stack_item["yield"].map(name => uhepp.yields[name].stat[bin_i + 1]).map(a => a*a).reduce((a, b) => a + b)
-          const stat = Math.sqrt(stat2)
+          const width = xScale(edges[bin_i]) - xScale(edges[bin_i - 1])
+          const stat = sumStat(uhepp.yields, stack_item["yield"], bin_i)
+
           const stat_up = yScale(y_value + stat) - yScale(y_value)
           const stat_down = yScale(y_value) - yScale(y_value - stat)
-          const gx = (xScale(uhepp.bins.edges[bin_i]) + xScale(uhepp.bins.edges[bin_i + 1])) / 2
+          const gx = (xScale(edges[bin_i - 1]) + xScale(edges[bin_i])) / 2
           const gy = yScale(bottom + y_value)
 
           if (stat && y_value) {
@@ -108,6 +108,20 @@ const Histogram = ({
 
 
       })
+
+			if (stack.type == "stepfilled") {
+				const whole_stack = stack.content.map(si => si["yield"]).flat()
+				const stat = sumStat(uhepp.yields, whole_stack, bin_i)
+				objects.push(<rect
+					key={`rect-${stack_index}-uncert-${bin_i}`}
+					x={xScale(edges[bin_i - 1])}
+					width={xScale(edges[bin_i]) - xScale(edges[bin_i - 1])}
+					y={yScale(bottom + stat)}
+					fill="url(#errorBand)"
+					height={yScale(bottom - stat) - yScale(bottom + stat)}
+					/>)
+			}
+
     })
   }
 
@@ -172,25 +186,6 @@ const EmbeddedMathJax = ({src, posX, posY, props}) => {
 					  dangerouslySetInnerHTML={{__html: rendered_svg.innerHTML}} { ...props}></g>
 }
 
-// const MixedText = ({children, posX, posY}) => {
-// 	const tokens = children.split("$")
-//   let offset = 0
-// 	return tokens.map((token, i) => {
-//     const is_tex = (i % 2 != 0)
-//     if (is_tex) {
-//       let [width, item] = EmbeddedMathJax({posX: posX + offset, posY: posY, src: token, props: {key: `emb-${i}`}})
-//       if (width > 0) {
-//         offset += width
-//       }
-//       return item
-//     } else {
-//       let item = <text key={`emb-${i}`} x={posX + offset} y={posY}>{token}</text>
-//       offset += length_metrix(token) * 8.5
-//       return item
-//     }
-// 	})
-// }
-
 const MixedText = ({children, x=0, y=0}) => {
   const embedded = `\\textsf{${children}}`
   const switched = embedded.replaceAll(/\$([^$]*)\$/g, "}$1\\textsf{")
@@ -204,8 +199,18 @@ const UheppHist = ({width, height, uhepp}) => {
   const [highlightedBin, setHighlightedBin] = useState(null);
   const [showTotal, setShowTotal] = useState(false);
 
+  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: 0,
+		top: 10,
 		bottom: 60,
 		left: 80,
 		right: 80,
@@ -220,12 +225,13 @@ const UheppHist = ({width, height, uhepp}) => {
 	})
 	const yScale = scaleLinear({
 		range: [yMax, 0],
-		domain: [0, getMaxBin(uhepp) * 1.3],
+		domain: [0, getMaxBin(post_uhepp) * 1.3],
 	})
 
+
   let i = 0
   let legend = []
-  uhepp.stacks.reverse().map(stack => {
+  post_uhepp.stacks.reverse().map(stack => {
       stack.content.reverse().map(stack_item => {
         if (stack.type == "points") {
             let color = stack_item.style ? stack_item.style.color : '#000'
@@ -269,18 +275,18 @@ const UheppHist = ({width, height, uhepp}) => {
                   />)
         }
         if (highlightedBin != null) {
-          const y_value = stack_item["yield"].map(name => uhepp.yields[name].base[highlightedBin + 1]).reduce((a, b) => a + b)
-          const stat2 = stack_item["yield"].map(name => uhepp.yields[name].stat[highlightedBin + 1]).map(a => a*a).reduce((a, b) => a + b)
-          const stat = Math.sqrt(stat2)
+          const y_value = sumBase(post_uhepp.yields, stack_item["yield"], highlightedBin)
+          const stat = sumStat(post_uhepp.yields, stack_item["yield"], highlightedBin)
+
           const label = y_value.toFixed(1) + " ± " + stat.toFixed(1)
-          legend.push(<text fontSize={12} textAnchor="end" x={width * 0.5 + 120} y={30 + i * 20}
+          legend.push(<text fontSize={12} textAnchor="end" x={width * 0.5 + 100} y={30 + i * 20}
                 key={`legend-text-${i}`}>{label}</text>)
         } else if (showTotal) {
-          const y_value = stack_item["yield"].map(name => uhepp.yields[name].base).flat().reduce((a, b) => a + b)
-          const stat2 = stack_item["yield"].map(name => uhepp.yields[name].stat).flat().map(a => a*a).reduce((a, b) => a + b)
-          const stat = Math.sqrt(stat2)
+          const y_value = sumBase(post_uhepp.yields, stack_item["yield"])
+          const stat = sumStat(post_uhepp.yields, stack_item["yield"])
+
           const label = y_value.toFixed(1) + " ± " + stat.toFixed(1)
-          legend.push(<text fontSize={12} textAnchor="end" x={width * 0.5 + 120} y={30 + i * 20}
+          legend.push(<text fontSize={12} textAnchor="end" x={width * 0.5 + 100} y={30 + i * 20}
                 key={`legend-text-${i}`}>{label}</text>)
 
         } else {
@@ -293,7 +299,7 @@ const UheppHist = ({width, height, uhepp}) => {
       })
       stack.content.reverse()
   })
-  uhepp.stacks.reverse()
+  post_uhepp.stacks.reverse()
 
 	return (
   <div style={{width: '100%', margin: '0 auto'}}>
@@ -304,13 +310,18 @@ const UheppHist = ({width, height, uhepp}) => {
         .uhepp-brand {'{font-style: italic; font-weight: bold}'}
         .uhepp-brand, .uhepp-label {'{font-size: 20px}'}
         </style>
+
+				<pattern id="errorBand" patternUnits="userSpaceOnUse" width="6" height="6">
+					<path d="M-1,1 l1,-1 M0,6 l6,-6 M5,7 l2,-2" style={{stroke: "#666",  strokeWidth: 1}} />
+				</pattern>
+
 				<Group top={margin.top} left={margin.left}>
 
          <text y={30} x={13} className="uhepp-brand">ATLAS</text>
          <text y={30} x={75} className="uhepp-label">Internal</text>
 
          <Histogram
-            uhepp={uhepp}
+            uhepp={post_uhepp}
             highlightedBin={highlightedBin}
             onMouseOverBin={binIndex => setHighlightedBin(binIndex)}
             stack_index={0}
diff --git a/uhepp-js/src/helpers/uhepp.js b/uhepp-js/src/helpers/uhepp.js
index f14fa04b4c2fcf5aa343bcc83a55776fdbadd452..945239c90d95d878060bdab3f73e4b58bf7747f1 100644
--- a/uhepp-js/src/helpers/uhepp.js
+++ b/uhepp-js/src/helpers/uhepp.js
@@ -110,7 +110,7 @@ export const sumStat = (yields, process_names, bin_index=null) => (
  *                        statistical uncetainties.
  * @param post            Callback that maps  values after summing them to undo
  *                        the effect of pre. Should be identify for the yields,
- *                        and the square root for statistical uncetainties.
+ *                        and the square root for statistical uncertainties.
  */
 export const rebin = (
   series,
@@ -144,4 +144,57 @@ export const rebin = (
   return result.map(x => post(x))
 }
 
-export const preprocessData = ( ) => 0
+const fromEntires = (entries) => {
+  let result = {}
+  entries.forEach(([key, value]) => {
+    result[key] = value
+  })
+  return result
+}
+
+/**
+ * Processes the yield objects and returns a new version
+ *
+ * The preprocessing encompasses rebinning, and the merging of overflow and
+ * underflow bins.
+ *
+ * @param yields             Uhepp yields objects to be processed
+ * @param old_edges          List of original bin boundaries
+ * @param new_edges          Optional list of new bin boundaries, must be subset
+ *                           of original boundaries
+ * @param include_underflow  If true, merge content of underflow to first bin
+ * @param include_overflow   If true, merge content of overflow to last bin
+ */
+export const preprocessData = ({
+  yields,
+  old_edges,
+  new_edges=null,
+  include_underflow,
+  include_overflow,
+}) => fromEntires(Object.entries(yields).map(([name, yield_obj]) => {
+  const eff_edges = new_edges ? new_edges : old_edges
+  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,
+  }
+  if (include_underflow) {
+    rebinned.base[1] += rebinned.base[0]
+    rebinned.base[0] = 0
+    if (rebinned.stat) {
+      rebinned.stat[1] = Math.sqrt(rebinned.stat[0]**2 + rebinned.stat[1]**2)
+      rebinned.stat[0] = 0
+    }
+  }
+  if (include_overflow) {
+    const b = rebinned.base.length - 1
+    rebinned.base[b - 1] += rebinned.base[b]
+    rebinned.base[b] = 0
+    if (rebinned.stat) {
+      rebinned.stat[b - 1] = Math.sqrt(rebinned.stat[b]**2 + rebinned.stat[b - 1]**2)
+      rebinned.stat[b] = 0
+    }
+  }
+  return [name, fromEntires(Object.entries(rebinned).filter(
+    ([name, values]) => !!values
+  ))]
+}))