diff --git a/Makefile b/Makefile
index 1d2b9b1..15f19fd 100644
--- a/Makefile
+++ b/Makefile
@@ -44,6 +44,7 @@ DEMO = \
 	 demo.cc \
 	 main.cc \
 	 syncview.cc \
+	 undo.cc \
 # END DEMO
 
 PARSERCHECK = \
diff --git a/globals.cc b/globals.cc
index 07a95c4..f0a2347 100644
--- a/globals.cc
+++ b/globals.cc
@@ -9,6 +9,7 @@
 #include "shaders.hh"
 #include "sync.hh"
 #include "texture.hh"
+#include "undo.hh"
 #include "window.hh"
 
 
@@ -20,6 +21,7 @@ T_OwnPtr< T_TextureManager > Globals::textures_;
 T_OwnPtr< T_ShaderManager > Globals::shaders_;
 T_OwnPtr< T_OutputDebugger > Globals::odbg_;
 T_OwnPtr< T_ScriptManager > Globals::ops_;
+T_OwnPtr< T_UndoManager > Globals::undo_;
 
 
 void Globals::Init( )
@@ -32,10 +34,12 @@ void Globals::Init( )
 	shaders_ = NewOwned< T_ShaderManager >( );
 	odbg_ = NewOwned< T_OutputDebugger >( );
 	ops_ = NewOwned< T_ScriptManager >( );
+	undo_ = NewOwned< T_UndoManager >( );
 }
 
 void Globals::Shutdown( )
 {
+	undo_.clear( );
 	ops_.clear( );
 	odbg_.clear( );
 	shaders_.clear( );
diff --git a/globals.hh b/globals.hh
index f98bd0f..e35866b 100644
--- a/globals.hh
+++ b/globals.hh
@@ -12,6 +12,7 @@ struct T_ShaderManager;
 struct T_OutputDebugger;
 struct T_SyncManager;
 struct T_ScriptManager;
+class T_UndoManager;
 
 
 struct Globals
@@ -27,6 +28,7 @@ struct Globals
 	static T_ShaderManager& Shaders( )	{ return *shaders_; }
 	static T_OutputDebugger& ODbg( )	{ return *odbg_; }
 	static T_ScriptManager& Ops( )		{ return *ops_; }
+	static T_UndoManager& Undo( )		{ return *undo_; }
 
     private:
 	static T_OwnPtr< T_FilesWatcher > watcher_;
@@ -37,4 +39,5 @@ struct Globals
 	static T_OwnPtr< T_TextureManager > textures_;
 	static T_OwnPtr< T_OutputDebugger > odbg_;
 	static T_OwnPtr< T_ScriptManager > ops_;
+	static T_OwnPtr< T_UndoManager > undo_;
 };
diff --git a/undo.cc b/undo.cc
new file mode 100644
index 0000000..ef27429
--- /dev/null
+++ b/undo.cc
@@ -0,0 +1,45 @@
+#include "externals.hh"
+#include "undo.hh"
+
+
+/*= A_UndoAction ===============================================================*/
+
+A_UndoAction::~A_UndoAction( )
+{}
+
+
+/*= T_UndoManager ==============================================================*/
+
+void T_UndoManager::undo( ) noexcept
+{
+	if ( pos_ > 0 ) {
+		pos_ --;
+		actions_[ ( start_ + pos_ ) % MaxUndo ]->undo( );
+	}
+}
+
+void T_UndoManager::redo( ) noexcept
+{
+	if ( pos_ < count_ ) {
+		pos_ ++;
+		actions_[ ( start_ + pos_ ) % MaxUndo ]->redo( );
+	}
+}
+
+void T_UndoManager::addAction(
+		P_UndoAction action ) noexcept
+{
+	assert( action );
+	while ( pos_ < count_ ) {
+		count_ --;
+		actions_[ ( start_ + count_ ) % MaxUndo ].clear( );
+	}
+	if ( count_ == MaxUndo ) {
+		actions_[ start_ ] = std::move( action );
+		start_ = ( start_ + 1 ) % MaxUndo;
+	} else {
+		actions_[ ( start_ + pos_ ) % MaxUndo ] = std::move( action );
+		count_ ++;
+		pos_ ++;
+	}
+}
diff --git a/undo.hh b/undo.hh
new file mode 100644
index 0000000..0180b45
--- /dev/null
+++ b/undo.hh
@@ -0,0 +1,57 @@
+#pragma once
+#ifndef REAL_BUILD
+# include "externals.hh"
+#endif
+
+
+// Interface for undo actions
+class A_UndoAction
+{
+    public:
+	virtual ~A_UndoAction( ) = 0;
+	virtual void undo( ) const noexcept = 0;
+	virtual void redo( ) const noexcept = 0;
+};
+using P_UndoAction = T_OwnPtr< A_UndoAction >;
+
+
+// Undo manager
+class T_UndoManager
+{
+    private:
+	static constexpr uint32_t MaxUndo = 1024;
+	
+	ebcl::T_StaticArray< P_UndoAction , MaxUndo > actions_;
+	uint32_t start_{ 0 };
+	uint32_t count_{ 0 };
+	uint32_t pos_{ 0 };
+
+    public:
+	T_UndoManager( ) noexcept = default;
+	DEF_MOVE( T_UndoManager );
+	NO_COPY( T_UndoManager );
+
+	// Undo last action
+	void undo( ) noexcept;
+	// Redo last undone action
+	void redo( ) noexcept;
+
+	// Add an action using a pointer
+	void addAction( P_UndoAction action ) noexcept;
+
+	// Construct and add an action
+	template<
+		typename T ,
+		typename... Args
+	> void add( Args&&... args ) noexcept;
+};
+
+
+template<
+	typename T ,
+	typename... Args
+> inline void T_UndoManager::add(
+		Args&&... args ) noexcept
+{
+	addAction( NewOwned< T >( std::forward< Args >( args ) ... ) );
+}