diff --git a/Calorimeter/CaloDigiAlgs/CMakeLists.txt b/Calorimeter/CaloDigiAlgs/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..42991aa03cc03af6e51ab161d36d1d61df1a7956
--- /dev/null
+++ b/Calorimeter/CaloDigiAlgs/CMakeLists.txt
@@ -0,0 +1,15 @@
+################################################################################
+# Package: CaloDigiAlgs
+################################################################################
+
+# Declare the package name:
+atlas_subdir( CaloDigiAlgs )
+
+# Component(s) in the package:
+atlas_add_component( CaloDigiAlgs
+                     src/*.cxx src/*.h
+                     src/components/*.cxx
+                     LINK_LIBRARIES AthenaBaseComps Identifier StoreGateLib WaveRawEvent FaserCaloSimEvent WaveDigiToolsLib)
+
+atlas_install_python_modules( python/*.py )
+
diff --git a/Calorimeter/CaloDigiAlgs/python/CaloDigiAlgsConfig.py b/Calorimeter/CaloDigiAlgs/python/CaloDigiAlgsConfig.py
new file mode 100644
index 0000000000000000000000000000000000000000..a1cf57803a4549e31dd4c341e77829194c97db9f
--- /dev/null
+++ b/Calorimeter/CaloDigiAlgs/python/CaloDigiAlgsConfig.py
@@ -0,0 +1,44 @@
+from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
+from AthenaConfiguration.ComponentFactory import CompFactory
+
+from OutputStreamAthenaPool.OutputStreamConfig import OutputStreamCfg
+
+
+# One stop shopping for normal FASER data
+def CaloWaveformDigitizationCfg(flags, naive = False):
+    """ Return all algorithms and tools for Waveform digitization """
+    acc = ComponentAccumulator()
+
+    if not flags.Input.isMC:
+        return
+
+    acc.merge(CaloWaveformDigiCfg(flags, "CaloWaveformDigiAlg"))
+    return acc
+
+# Return configured digitization algorithm from SIM hits
+def CaloWaveformDigiCfg(flags, name="CaloWaveformDigiAlg", **kwargs):
+
+    acc = ComponentAccumulator()
+
+    tool = CompFactory.WaveformDigitisationTool(name="CaloWaveformDigtisationTool", **kwargs)
+    
+    kwargs.setdefault("CaloHitContainerKey", "EcalHits")
+    kwargs.setdefault("WaveformContainerKey", "CaloWaveforms")
+
+    digiAlg = CompFactory.CaloWaveformDigiAlg(name, **kwargs)
+    kwargs.setdefault("WaveformDigitisationTool", tool)
+
+    acc.addEventAlgo(digiAlg)
+
+    return acc
+
+def CaloWaveformDigitizationOutputCfg(flags, **kwargs):
+    """ Return ComponentAccumulator with output for Waveform Digi"""
+    acc = ComponentAccumulator()
+    ItemList = [
+        "RawWaveformContainer#*"
+    ]
+    acc.merge(OutputStreamCfg(flags, "RDO", ItemList))
+    # ostream = acc.getEventAlgo("OutputStreamRDO")
+    # ostream.TakeItemsFromInput = True # Don't know what this does
+    return acc
diff --git a/Calorimeter/CaloDigiAlgs/src/CaloWaveformDigiAlg.cxx b/Calorimeter/CaloDigiAlgs/src/CaloWaveformDigiAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..5faf298ff2de05fdeb3269b0088778ed144d845e
--- /dev/null
+++ b/Calorimeter/CaloDigiAlgs/src/CaloWaveformDigiAlg.cxx
@@ -0,0 +1,76 @@
+#include "CaloWaveformDigiAlg.h"
+
+#include "Identifier/Identifier.h"
+
+#include <vector>
+#include <map>
+
+CaloWaveformDigiAlg::CaloWaveformDigiAlg(const std::string& name, 
+					 ISvcLocator* pSvcLocator)
+  : AthReentrantAlgorithm(name, pSvcLocator) { 
+
+}
+
+StatusCode 
+CaloWaveformDigiAlg::initialize() {
+  ATH_MSG_INFO(name() << "::initalize()" );
+
+  // Initalize tools
+  ATH_CHECK( m_digiTool.retrieve() );
+
+
+  // Set key to read waveform from
+  ATH_CHECK( m_caloHitContainerKey.initialize() );
+
+  // Set key to write container
+  ATH_CHECK( m_waveformContainerKey.initialize() );
+
+  // Will eventually depend on the type of detector
+  // TODO: Vary time at which centre it?
+  // TODO: Change params compared to scint
+  m_kernel = new TF1("PDF", " ROOT::Math::crystalball_pdf(x, -0.25, 10, 4, 900)", 0, 1200);
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode 
+CaloWaveformDigiAlg::finalize() {
+  ATH_MSG_INFO(name() << "::finalize()");
+
+  delete m_kernel;
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode 
+CaloWaveformDigiAlg::execute(const EventContext& ctx) const {
+  ATH_MSG_DEBUG("Executing");
+
+  ATH_MSG_DEBUG("Run: " << ctx.eventID().run_number() 
+		<< " Event: " << ctx.eventID().event_number());
+
+  // Find the input HIT collection
+  SG::ReadHandle<CaloHitCollection> caloHitHandle(m_caloHitContainerKey, ctx);
+
+  ATH_CHECK( caloHitHandle.isValid() );
+  ATH_MSG_DEBUG("Found ReadHandle for CaloHitCollection " << m_caloHitContainerKey);
+
+  if (caloHitHandle->size() == 0) {
+    ATH_MSG_DEBUG("CaloHitCollection found with zero length!");
+    return StatusCode::SUCCESS;
+  }
+
+  // Find the output waveform container
+  SG::WriteHandle<RawWaveformContainer> waveformContainerHandle(m_waveformContainerKey, ctx);
+  ATH_CHECK( waveformContainerHandle.record( std::make_unique<RawWaveformContainer>()) );
+
+  ATH_MSG_DEBUG("WaveformsContainer '" << waveformContainerHandle.name() << "' initialized");
+
+  // Digitise the hits
+  CHECK( m_digiTool->digitise<CaloHitCollection>(caloHitHandle.ptr(),
+						  waveformContainerHandle.ptr(), m_kernel) );
+
+  ATH_MSG_DEBUG("WaveformsHitContainer " << waveformContainerHandle.name() << "' filled with "<< waveformContainerHandle->size() <<" items");
+
+  return StatusCode::SUCCESS;
+}
diff --git a/Calorimeter/CaloDigiAlgs/src/CaloWaveformDigiAlg.h b/Calorimeter/CaloDigiAlgs/src/CaloWaveformDigiAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..a1ca84f42d7ac3606f043bf5ddd98676b37bbfa7
--- /dev/null
+++ b/Calorimeter/CaloDigiAlgs/src/CaloWaveformDigiAlg.h
@@ -0,0 +1,84 @@
+#ifndef CALODIGIALGS_CALOWAVEFORMDIGIALG_H
+#define CALODIGIALGS_CALOWAVEFORMDIGIALG_H
+
+// Base class
+#include "AthenaBaseComps/AthReentrantAlgorithm.h"
+
+// Data classes
+#include "WaveRawEvent/RawWaveformContainer.h"
+#include "FaserCaloSimEvent/CaloHitCollection.h"
+
+// Tool classes
+#include "WaveDigiTools/IWaveformDigitisationTool.h"
+
+// Handles
+#include "StoreGate/ReadHandleKey.h"
+#include "StoreGate/WriteHandleKey.h"
+
+// Gaudi
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+// ROOT
+#include "TF1.h"
+
+// STL
+#include <string>
+
+class CaloWaveformDigiAlg : public AthReentrantAlgorithm {
+
+ public:
+  // Constructor
+  CaloWaveformDigiAlg(const std::string& name, ISvcLocator* pSvcLocator);
+  virtual ~CaloWaveformDigiAlg() = default;
+
+  /** @name Usual algorithm methods */
+  //@{
+  virtual StatusCode initialize() override;
+  virtual StatusCode execute(const EventContext& ctx) const override;
+  virtual StatusCode finalize() override;
+  //@}
+
+ private:
+
+  /** @name Disallow default instantiation, copy, assignment */
+  //@{
+  CaloWaveformDigiAlg() = delete;
+  CaloWaveformDigiAlg(const CaloWaveformDigiAlg&) = delete;
+  CaloWaveformDigiAlg &operator=(const CaloWaveformDigiAlg&) = delete;
+  //@}
+
+
+  /// Kernel PDF
+  TF1* m_kernel;
+
+
+  /**
+   * @name Digitisation tool
+   */
+  ToolHandle<IWaveformDigitisationTool> m_digiTool
+    {this, "WaveformDigitisationTool", "WaveformDigitisationTool"};
+
+
+  /**
+   * @name Input HITS using SG::ReadHandleKey
+   */
+  //@{
+
+  SG::ReadHandleKey<CaloHitCollection> m_caloHitContainerKey 
+  {this, "CaloHitContainerKey", ""};
+
+  //@}
+
+
+  /**
+   * @name Output data using SG::WriteHandleKey
+   */
+  //@{
+  SG::WriteHandleKey<RawWaveformContainer> m_waveformContainerKey
+    {this, "WaveformContainerKey", ""};
+  //@}
+
+};
+
+#endif // CALODIGIALGS_CALODIGIALG_H
diff --git a/Calorimeter/CaloDigiAlgs/src/components/CaloDigiAlgs_entries.cxx b/Calorimeter/CaloDigiAlgs/src/components/CaloDigiAlgs_entries.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..832cfbb096ffdb13430ba92dc2bfc7b1125bd53c
--- /dev/null
+++ b/Calorimeter/CaloDigiAlgs/src/components/CaloDigiAlgs_entries.cxx
@@ -0,0 +1,2 @@
+#include "../CaloWaveformDigiAlg.h"
+DECLARE_COMPONENT( CaloWaveformDigiAlg )
diff --git a/Scintillator/ScintDigiAlgs/CMakeLists.txt b/Scintillator/ScintDigiAlgs/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..43695c5f90d96053a8b44fa3ac41c0f4c159ad99
--- /dev/null
+++ b/Scintillator/ScintDigiAlgs/CMakeLists.txt
@@ -0,0 +1,15 @@
+################################################################################
+# Package: ScintDigiAlgs
+################################################################################
+
+# Declare the package name:
+atlas_subdir( ScintDigiAlgs )
+
+# Component(s) in the package:
+atlas_add_component( ScintDigiAlgs
+                     src/*.cxx src/*.h
+                     src/components/*.cxx
+                     LINK_LIBRARIES AthenaBaseComps Identifier StoreGateLib WaveRawEvent ScintSimEvent WaveDigiToolsLib)
+
+atlas_install_python_modules( python/*.py )
+
diff --git a/Scintillator/ScintDigiAlgs/python/ScintDigiAlgsConfig.py b/Scintillator/ScintDigiAlgs/python/ScintDigiAlgsConfig.py
new file mode 100644
index 0000000000000000000000000000000000000000..5f8e0c6ed9f2870652f567b839bb6a59dec437a3
--- /dev/null
+++ b/Scintillator/ScintDigiAlgs/python/ScintDigiAlgsConfig.py
@@ -0,0 +1,48 @@
+from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
+from AthenaConfiguration.ComponentFactory import CompFactory
+
+from OutputStreamAthenaPool.OutputStreamConfig import OutputStreamCfg
+
+
+# One stop shopping for normal FASER data
+def ScintWaveformDigitizationCfg(flags, naive = False):
+    """ Return all algorithms and tools for Waveform digitization """
+    acc = ComponentAccumulator()
+
+    if not flags.Input.isMC:
+        return
+
+    if "TB" not in flags.GeoModel.FaserVersion:
+        acc.merge(ScintWaveformDigiCfg(flags, "TimingWaveformDigiAlg", "Trigger"))
+    acc.merge(ScintWaveformDigiCfg(flags, "VetoWaveformDigiAlg", "Veto"))
+    acc.merge(ScintWaveformDigiCfg(flags, "PreshowerWaveformDigiAlg", "Preshower"))
+    return acc
+
+# Return configured digitization algorithm from SIM hits
+# Specify data source (Veto, Trigger, Preshower)
+def ScintWaveformDigiCfg(flags, name="ScintWaveformDigiAlg", source="", **kwargs):
+
+    acc = ComponentAccumulator()
+
+    tool = CompFactory.WaveformDigitisationTool(name=source+"WaveformDigtisationTool", **kwargs)
+    
+    kwargs.setdefault("ScintHitContainerKey", source+"Hits")
+    kwargs.setdefault("WaveformContainerKey", source+"Waveforms")
+
+    digiAlg = CompFactory.ScintWaveformDigiAlg(name, **kwargs)
+    kwargs.setdefault("WaveformDigitisationTool", tool)
+
+    acc.addEventAlgo(digiAlg)
+
+    return acc
+
+def ScintWaveformDigitizationOutputCfg(flags, **kwargs):
+    """ Return ComponentAccumulator with output for Waveform Digi"""
+    acc = ComponentAccumulator()
+    ItemList = [
+        "RawWaveformContainer#*"
+    ]
+    acc.merge(OutputStreamCfg(flags, "RDO", ItemList))
+    # ostream = acc.getEventAlgo("OutputStreamRDO")
+    # ostream.TakeItemsFromInput = True # Don't know what this does
+    return acc
diff --git a/Scintillator/ScintDigiAlgs/src/ScintWaveformDigiAlg.cxx b/Scintillator/ScintDigiAlgs/src/ScintWaveformDigiAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..e09a778a33c3a9614d8fc5e584abdfcc51a67032
--- /dev/null
+++ b/Scintillator/ScintDigiAlgs/src/ScintWaveformDigiAlg.cxx
@@ -0,0 +1,76 @@
+#include "ScintWaveformDigiAlg.h"
+
+#include "Identifier/Identifier.h"
+
+#include <vector>
+#include <map>
+
+ScintWaveformDigiAlg::ScintWaveformDigiAlg(const std::string& name, 
+					 ISvcLocator* pSvcLocator)
+  : AthReentrantAlgorithm(name, pSvcLocator) { 
+
+}
+
+StatusCode 
+ScintWaveformDigiAlg::initialize() {
+  ATH_MSG_INFO(name() << "::initalize()" );
+
+  // Initalize tools
+  ATH_CHECK( m_digiTool.retrieve() );
+
+
+  // Set key to read waveform from
+  ATH_CHECK( m_scintHitContainerKey.initialize() );
+
+  // Set key to write container
+  ATH_CHECK( m_waveformContainerKey.initialize() );
+
+  // Will eventually depend on the type of detector
+  // TODO: Vary time at which centre it?
+  // TODO: Better parameters
+  m_kernel = new TF1("PDF", " ROOT::Math::crystalball_pdf(x, -0.25, 10, 4, 900)", 0, 1200);
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode 
+ScintWaveformDigiAlg::finalize() {
+  ATH_MSG_INFO(name() << "::finalize()");
+
+  delete m_kernel;
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode 
+ScintWaveformDigiAlg::execute(const EventContext& ctx) const {
+  ATH_MSG_DEBUG("Executing");
+
+  ATH_MSG_DEBUG("Run: " << ctx.eventID().run_number() 
+		<< " Event: " << ctx.eventID().event_number());
+
+  // Find the input HIT collection
+  SG::ReadHandle<ScintHitCollection> scintHitHandle(m_scintHitContainerKey, ctx);
+
+  ATH_CHECK( scintHitHandle.isValid() );
+  ATH_MSG_DEBUG("Found ReadHandle for ScintHitCollection " << m_scintHitContainerKey);
+
+  if (scintHitHandle->size() == 0) {
+    ATH_MSG_DEBUG("ScintHitCollection found with zero length!");
+    return StatusCode::SUCCESS;
+  }
+
+  // Find the output waveform container
+  SG::WriteHandle<RawWaveformContainer> waveformContainerHandle(m_waveformContainerKey, ctx);
+  ATH_CHECK( waveformContainerHandle.record( std::make_unique<RawWaveformContainer>()) );
+
+  ATH_MSG_DEBUG("WaveformsContainer '" << waveformContainerHandle.name() << "' initialized");
+
+  // Digitise the hits
+  CHECK( m_digiTool->digitise<ScintHitCollection>(scintHitHandle.ptr(),
+						  waveformContainerHandle.ptr(), m_kernel) );
+
+  ATH_MSG_DEBUG("WaveformsHitContainer " << waveformContainerHandle.name() << "' filled with "<< waveformContainerHandle->size() <<" items");
+
+  return StatusCode::SUCCESS;
+}
diff --git a/Scintillator/ScintDigiAlgs/src/ScintWaveformDigiAlg.h b/Scintillator/ScintDigiAlgs/src/ScintWaveformDigiAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..70e333a952912af5e50240eb606b83cd01fd9e27
--- /dev/null
+++ b/Scintillator/ScintDigiAlgs/src/ScintWaveformDigiAlg.h
@@ -0,0 +1,84 @@
+#ifndef SCINTDIGIALGS_SCINTWAVEFORMDIGIALG_H
+#define SCINTDIGIALGS_SCINTWAVEFORMDIGIALG_H
+
+// Base class
+#include "AthenaBaseComps/AthReentrantAlgorithm.h"
+
+// Data classes
+#include "WaveRawEvent/RawWaveformContainer.h"
+#include "ScintSimEvent/ScintHitCollection.h"
+
+// Tool classes
+#include "WaveDigiTools/IWaveformDigitisationTool.h"
+
+// Handles
+#include "StoreGate/ReadHandleKey.h"
+#include "StoreGate/WriteHandleKey.h"
+
+// Gaudi
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
+// ROOT
+#include "TF1.h"
+
+// STL
+#include <string>
+
+class ScintWaveformDigiAlg : public AthReentrantAlgorithm {
+
+ public:
+  // Constructor
+  ScintWaveformDigiAlg(const std::string& name, ISvcLocator* pSvcLocator);
+  virtual ~ScintWaveformDigiAlg() = default;
+
+  /** @name Usual algorithm methods */
+  //@{
+  virtual StatusCode initialize() override;
+  virtual StatusCode execute(const EventContext& ctx) const override;
+  virtual StatusCode finalize() override;
+  //@}
+
+ private:
+
+  /** @name Disallow default instantiation, copy, assignment */
+  //@{
+  ScintWaveformDigiAlg() = delete;
+  ScintWaveformDigiAlg(const ScintWaveformDigiAlg&) = delete;
+  ScintWaveformDigiAlg &operator=(const ScintWaveformDigiAlg&) = delete;
+  //@}
+
+
+  /// Kernel PDF
+  TF1* m_kernel;
+
+
+  /**
+   * @name Digitisation tool
+   */
+  ToolHandle<IWaveformDigitisationTool> m_digiTool
+    {this, "WaveformDigitisationTool", "WaveformDigitisationTool"};
+
+
+  /**
+   * @name Input HITS using SG::ReadHandleKey
+   */
+  //@{
+
+  SG::ReadHandleKey<ScintHitCollection> m_scintHitContainerKey 
+  {this, "ScintHitContainerKey", ""};
+
+  //@}
+
+
+  /**
+   * @name Output data using SG::WriteHandleKey
+   */
+  //@{
+  SG::WriteHandleKey<RawWaveformContainer> m_waveformContainerKey
+    {this, "WaveformContainerKey", ""};
+  //@}
+
+};
+
+#endif // SCINTDIGIALGS_SCINTDIGIALG_H
diff --git a/Scintillator/ScintDigiAlgs/src/components/ScintDigiAlgs_entries.cxx b/Scintillator/ScintDigiAlgs/src/components/ScintDigiAlgs_entries.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..3a8deff1f06b692e1cf0928472fb12426274f059
--- /dev/null
+++ b/Scintillator/ScintDigiAlgs/src/components/ScintDigiAlgs_entries.cxx
@@ -0,0 +1,2 @@
+#include "../ScintWaveformDigiAlg.h"
+DECLARE_COMPONENT( ScintWaveformDigiAlg )
diff --git a/Waveform/WaveDigiTools/CMakeLists.txt b/Waveform/WaveDigiTools/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..692fdb69bc14451ba5a6a660d011bad5b14b66e5
--- /dev/null
+++ b/Waveform/WaveDigiTools/CMakeLists.txt
@@ -0,0 +1,25 @@
+################################################################################
+# Package: WaveDigiTools
+################################################################################
+
+# Declare the package name:
+atlas_subdir( WaveDigiTools )
+
+# External dependencies:
+find_package( ROOT )
+
+# Component(s) in the package:
+atlas_add_library( WaveDigiToolsLib
+                   WaveDigiTools/*.h src/*.cxx src/*.h
+                   PUBLIC_HEADERS WaveDigiTools
+                   PRIVATE_INCLUDE_DIRS ${ROOT_INCLUDE_DIRS}
+                   LINK_LIBRARIES AthenaBaseComps AthenaKernel GeoPrimitives WaveRawEvent
+                   PRIVATE_LINK_LIBRARIES ${ROOT_LIBRARIES}
+		   )
+
+atlas_add_component( WaveDigiTools
+		     src/components/*.cxx 
+		     INCLUDE_DIRS ${ROOT_INCLUDE_DIRS}
+                     LINK_LIBRARIES ${ROOT_LIBRARIES} AthenaBaseComps GaudiKernel WaveDigiToolsLib )
+
+
diff --git a/Waveform/WaveDigiTools/WaveDigiTools/IWaveformDigitisationTool.h b/Waveform/WaveDigiTools/WaveDigiTools/IWaveformDigitisationTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..3e85ff839a04f6d02c794fa74567841450c9cfeb
--- /dev/null
+++ b/Waveform/WaveDigiTools/WaveDigiTools/IWaveformDigitisationTool.h
@@ -0,0 +1,55 @@
+/*
+  Copyright (C) 2021 CERN for the benefit of the FASER collaboration
+*/
+
+/**
+ * @file IWaveformDigitisationTool.h
+ * Header file for the IWaveformDigitisationTool class
+ * @author Carl Gwilliam, 2021
+ */
+
+
+#ifndef WAVEDIGITOOLS_IWAVEFORMDIGITISATIONTOOL_H
+#define WAVEDIGITOOLS_IWAVEFORMDIGITISATIONTOOL_H
+
+// Base class
+#include "GaudiKernel/IAlgTool.h"
+#include "GaudiKernel/ToolHandle.h"
+
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/IMessageSvc.h"
+#include "GaudiKernel/MsgStream.h"
+
+#include "WaveRawEvent/RawWaveformContainer.h"
+#include "WaveRawEvent/RawWaveform.h"
+
+#include "TF1.h"
+
+///Interface for waveform digitisation tools
+class IWaveformDigitisationTool : virtual public IAlgTool 
+{
+public:
+
+  // InterfaceID
+  DeclareInterfaceID(IWaveformDigitisationTool, 1, 0);
+
+  IWaveformDigitisationTool():
+    m_msgSvc         ( "MessageSvc",   "ITrkEventCnvTool" )
+  {}
+
+  virtual ~IWaveformDigitisationTool() = default;
+
+  // Digitise HITS to Raw waveform
+  template<class CONT>
+  StatusCode digitise(const CONT* hitCollection, 
+		      RawWaveformContainer* waveContainer, TF1* kernel) const;
+
+private:
+  ServiceHandle<IMessageSvc>      m_msgSvc;
+
+};
+
+#include "WaveDigiTools/IWaveformDigitisationTool.icc"
+
+
+#endif //WAVEDIGITOOLS_IWAVEFORMDIGITISATIONTOOL_H
diff --git a/Waveform/WaveDigiTools/WaveDigiTools/IWaveformDigitisationTool.icc b/Waveform/WaveDigiTools/WaveDigiTools/IWaveformDigitisationTool.icc
new file mode 100644
index 0000000000000000000000000000000000000000..57d4839bda5f2d30286ec412e3f01c92ce353b11
--- /dev/null
+++ b/Waveform/WaveDigiTools/WaveDigiTools/IWaveformDigitisationTool.icc
@@ -0,0 +1,51 @@
+#include <vector>
+#include <map>
+
+template<class CONT>
+StatusCode IWaveformDigitisationTool::digitise(const CONT* hitCollection,
+					       RawWaveformContainer* container, TF1* kernel) const {
+
+
+  // Check the container
+  if (!container) {
+    MsgStream log(&(*m_msgSvc), name());
+    log << MSG::ERROR << "HitCollection passed to digitise() is null!" << endmsg;
+    return StatusCode::FAILURE;
+  }
+
+  unsigned int size = 600;  // TODO: how know the correct number of time samples?
+  std::vector<float> time(size);  
+  for (unsigned int i=0; i<size; i++) time[i] = 2.*i;
+
+  std::map<unsigned int, std::vector<uint16_t>> waveforms;
+  unsigned int baseline = 8000; // TODO: vary this + add noise
+
+  // Loop over time samples
+  for (const auto& t : time) {
+    std::map<unsigned int, float> counts;
+   
+    // Convolve hit energy with kernel and sum for each ID (i.e. channel)
+    for (const auto& hit : *hitCollection) { 
+      counts[hit.identify()] += kernel->Eval(t) * hit.energyLoss();
+      //std::cout << "HIT " << hit.identify() << " @ " << t << ": " <<  kernel->Eval(t) << " " << hit.energyLoss() << " -> " << counts[hit.identify()] << std::endl;
+    }
+
+    // Add count to correct waveform vec
+    for (const auto& c : counts) {
+      waveforms[c.first].push_back(baseline - c.second);
+      //std::cout << "ADC " << c.first << " @ " << t << ": " << baseline - c.second << std::endl;
+    }
+  }
+
+  // Loop over wavefrom vecs to make and store waveform
+  for (const auto& w : waveforms) {
+    RawWaveform* wfm = new RawWaveform();
+    wfm->setWaveform(0, w.second);
+    wfm->setIdentifier(Identifier(w.first));
+    wfm->setSamples(size);
+    container->push_back(wfm);
+  }
+
+
+  return StatusCode::SUCCESS;
+}
diff --git a/Waveform/WaveDigiTools/share/WaveformDigiAndRecoExample_jobOptions.py b/Waveform/WaveDigiTools/share/WaveformDigiAndRecoExample_jobOptions.py
new file mode 100644
index 0000000000000000000000000000000000000000..285136cd51b4f7d53076f27351e21833ba6b0751
--- /dev/null
+++ b/Waveform/WaveDigiTools/share/WaveformDigiAndRecoExample_jobOptions.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+
+import sys
+
+if __name__ == "__main__":
+
+    fileroot = "output"
+    if(len(sys.argv > 1))
+       filename = sys.argv[1]
+
+    doRDO = False
+    if(len(sys.argv > 2))
+       filename = sys.argv[2]
+
+    from AthenaCommon.Logging import log, logging
+    from AthenaCommon.Constants import DEBUG, VERBOSE, INFO
+    from CalypsoConfiguration.AllConfigFlags import ConfigFlags
+    from AthenaCommon.Configurable import Configurable
+        
+    Configurable.configurableRun3Behavior = True
+
+    log.setLevel(VERBOSE)
+    
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"             # Always needed; must match FaserVersion
+    ConfigFlags.IOVDb.DatabaseInstance = "OFLP200"               # Use MC conditions for now
+    ConfigFlags.Input.ProjectName = "mc21"                       # Needed to bypass autoconfig
+    ConfigFlags.Input.isMC = True                                # Needed to bypass autoconfig
+    ConfigFlags.GeoModel.FaserVersion     = "FASER-TB00"         # FASER geometry
+    ConfigFlags.Common.isOnline = False
+    ConfigFlags.GeoModel.Align.Dynamic = False
+    
+    ConfigFlags.Input.Files = [
+        "/bundle/data/FASER/LC_output/BatchOutput/TestBeam/ScanPoint8/Elec100GeV.s8.p8.r5x13.8y22.7.split1.TB.SIM.root"
+        ]
+
+    if doRDO:
+        ConfigFlags.Output.RDOFileName = f"{fileroot}.RDO.root"
+    else:
+        ConfigFlags.addFlag("Output.xAODFileName", f"{fileroot}.xAOD.root")
+        ConfigFlags.Output.ESDFileName = f"{fileroot}.ESD.root"
+
+    ConfigFlags.lock()
+
+    from CalypsoConfiguration.MainServicesConfig import MainServicesCfg
+    acc = MainServicesCfg(ConfigFlags)
+
+    from AthenaPoolCnvSvc.PoolReadConfig import PoolReadCfg
+    from AthenaPoolCnvSvc.PoolWriteConfig import PoolWriteCfg
+
+    acc.merge(PoolReadCfg(ConfigFlags))
+    acc.merge(PoolWriteCfg(ConfigFlags))
+
+    if doRDO:
+         from OutputStreamAthenaPool.OutputStreamConfig import OutputStreamCfg
+         itemList = [
+             "RawWaveformContainer#*"
+             ]
+         acc.merge(OutputStreamCfg(ConfigFlags, "RDO", itemList,disableEventTag=True))
+
+
+    else:
+        from OutputStreamAthenaPool.OutputStreamConfig import OutputStreamCfg
+        itemList = [
+            "xAOD::EventInfo#*",
+            "xAOD::WaveformHitContainer#*",
+            "xAOD::WaveformHitAuxContainer#*",
+            ]
+
+        acc.merge(OutputStreamCfg(ConfigFlags, "xAOD", itemList, disableEventTag=True))
+
+    from ScintDigiAlgs.ScintDigiAlgsConfig import ScintWaveformDigitizationCfg
+    acc.merge(ScintWaveformDigitizationCfg(ConfigFlags))
+
+    from CaloDigiAlgs.CaloDigiAlgsConfig import CaloWaveformDigitizationCfg
+    acc.merge(CaloWaveformDigitizationCfg(ConfigFlags))
+
+    if not doRDO:
+        from WaveRecAlgs.WaveRecAlgsConfig import WaveformReconstructionCfg
+        acc.merge(WaveformReconstructionCfg(ConfigFlags))
+
+    #acc.foreach_component("*").OutputLevel = VERBOSE
+
+    # Execute and finish
+    sc = acc.run(maxEvents=100)
+
+    # Success should be 0
+    sys.exit(not sc.isSuccess())
diff --git a/Waveform/WaveDigiTools/src/WaveformDigitisationTool.cxx b/Waveform/WaveDigiTools/src/WaveformDigitisationTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..e4776da3bf1b140fa1c614642d939f46557b0ba6
--- /dev/null
+++ b/Waveform/WaveDigiTools/src/WaveformDigitisationTool.cxx
@@ -0,0 +1,26 @@
+/*
+  Copyright (C) 2021 CERN for the benefit of the FASER collaboration
+*/
+
+/**
+ * @file WaveformDigitisationTool.cxx
+ * Implementation file for the WaveformDigitisationTool class
+ * @ author C. Gwilliam, 2021
+ **/
+
+#include "WaveformDigitisationTool.h"
+
+// Constructor
+WaveformDigitisationTool::WaveformDigitisationTool(const std::string& type, const std::string& name, const IInterface* parent) :
+  base_class(type, name, parent)
+{
+}
+
+// Initialization
+StatusCode
+WaveformDigitisationTool::initialize() {
+  ATH_MSG_INFO( name() << "::initalize()" );
+  return StatusCode::SUCCESS;
+}
+
+
diff --git a/Waveform/WaveDigiTools/src/WaveformDigitisationTool.h b/Waveform/WaveDigiTools/src/WaveformDigitisationTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..8a5ba71f3dd124fcdd2c6b4b8124ee96591512da
--- /dev/null
+++ b/Waveform/WaveDigiTools/src/WaveformDigitisationTool.h
@@ -0,0 +1,36 @@
+/*
+   Copyright (C) 2021 CERN for the benefit of the FASER collaboration
+*/
+
+/** @file WaveformDigitisationTool.h
+ *  Header file for WaveformDigitisationTool.h
+ *
+ */
+#ifndef WAVEDIGITOOLS_WAVEFORMDIGITISATIONTOOL_H
+#define WAVEDIGITOOLS_WAVEFORMDIGITISATIONTOOL_H
+
+//Athena
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "WaveDigiTools/IWaveformDigitisationTool.h"
+
+//Gaudi
+#include "GaudiKernel/ToolHandle.h"
+
+//STL
+
+class WaveformDigitisationTool: public extends<AthAlgTool, IWaveformDigitisationTool> {
+ public:
+
+  /// Normal constructor for an AlgTool; 'properties' are also declared here
+ WaveformDigitisationTool(const std::string& type, 
+			  const std::string& name, const IInterface* parent);
+
+  /// Retrieve the necessary services in initialize
+  StatusCode initialize();
+
+ private:
+  // None
+
+};
+
+#endif // WAVEDIGITOOLS_WAVEFORMDIGITISATIONTOOL_H
diff --git a/Waveform/WaveDigiTools/src/components/WaveDigiTools_entries.cxx b/Waveform/WaveDigiTools/src/components/WaveDigiTools_entries.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..169476d905bb3e3a56de0d15aac7b7d13c3bdeb1
--- /dev/null
+++ b/Waveform/WaveDigiTools/src/components/WaveDigiTools_entries.cxx
@@ -0,0 +1,3 @@
+#include "../WaveformDigitisationTool.h"
+
+DECLARE_COMPONENT( WaveformDigitisationTool )
diff --git a/Waveform/WaveRecAlgs/python/WaveRecAlgsConfig.py b/Waveform/WaveRecAlgs/python/WaveRecAlgsConfig.py
index 39a99ed0c39d01e08c5f555e4e3efd98e0bc70a1..c4acec4bd4fe3c6fedc17e9c8a4c681f0c83f476 100644
--- a/Waveform/WaveRecAlgs/python/WaveRecAlgsConfig.py
+++ b/Waveform/WaveRecAlgs/python/WaveRecAlgsConfig.py
@@ -15,20 +15,23 @@ def WaveformReconstructionCfg(flags, naive = True):
     """ Return all algorithms and tools for Waveform reconstruction """
     acc = ComponentAccumulator()
 
+    if not flags.Input.isMC:
+        acc.merge(WaveformClockRecCfg(flags, "ClockRecAlg"))
+
     if flags.Input.isMC  and naive:
-        if not "TB" in flags.GeoModel.FaserVersion:
+        if "TB" not in flags.GeoModel.FaserVersion:
             acc.merge(PseudoScintHitToWaveformRecCfg(flags, "PseudoTimingHitWaveformRecAlg", "Trigger"))
         acc.merge(PseudoScintHitToWaveformRecCfg(flags, "PseudoVetoHitToWaveformRecAlg", "Veto"))
         acc.merge(PseudoScintHitToWaveformRecCfg(flags, "PseudoPresehowerHitWaveformRecAlg", "Preshower"))
         acc.merge(PseudoCaloHitToWaveformRecCfg(flags, "PseudoCaloHitWaveformRecAlg"))                
         return acc
 
-    acc.merge(WaveformClockRecCfg(flags, "ClockRecAlg"))
-
+    if "TB" not in flags.GeoModel.FaserVersion:
+        acc.merge(WaveformHitRecCfg(flags, "TimingWaveformRecAlg", "Trigger"))
     acc.merge(WaveformHitRecCfg(flags, "VetoWaveformRecAlg", "Veto"))
-    acc.merge(WaveformHitRecCfg(flags, "TimingWaveformRecAlg", "Trigger"))
     acc.merge(WaveformHitRecCfg(flags, "PreshowerWaveformRecAlg", "Preshower"))
     acc.merge(WaveformHitRecCfg(flags, "CaloWaveformRecAlg", "Calo"))
+
     return acc
 
 # Return configured WaveformClock reconstruction algorithm
@@ -52,7 +55,13 @@ def WaveformHitRecCfg(flags, name="WaveformRecAlg", source="", **kwargs):
 
     acc = ComponentAccumulator()
 
+    if flags.Input.isMC:
+        kwargs.setdefault("PeakThreshold", 5)
+
     tool = WaveformReconstructionTool(name=source+"WaveformRecTool", **kwargs)
+
+    kwargs.pop("PeakThreshold")
+
     kwargs.setdefault("WaveformContainerKey", source+"Waveforms")
     kwargs.setdefault("WaveformHitContainerKey", source+"WaveformHits")
     kwargs.setdefault("WaveformReconstructionTool", tool)