diff --git a/toonz/sources/common/tassistantsimage/tassistantsimage.cpp b/toonz/sources/common/tassistantsimage/tassistantsimage.cpp
deleted file mode 100644
index 79bf506..0000000
--- a/toonz/sources/common/tassistantsimage/tassistantsimage.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-#include "tassistantsimage.h"
-
-//---------------------------------------------------------
-
-TAssistantsImage::TAssistantsImage()
-  { }
-
-//---------------------------------------------------------
-
-TAssistantsImage::TAssistantsImage(const TAssistantsImage &other):
-  m_assistants(*Reader(other))
-  { }
-
-//---------------------------------------------------------
-
-TAssistantsImage::~TAssistantsImage()
-  { }
-
-//---------------------------------------------------------
-
-TImage*
-TAssistantsImage::cloneImage() const
-  { return new TAssistantsImage(*this); }
-
-//---------------------------------------------------------
-
-TRectD
-TAssistantsImage::getBBox() const
-  { return TRectD(); }
-
-//---------------------------------------------------------
diff --git a/toonz/sources/common/tmetaimage/tmetaimage.cpp b/toonz/sources/common/tmetaimage/tmetaimage.cpp
new file mode 100644
index 0000000..0c762b6
--- /dev/null
+++ b/toonz/sources/common/tmetaimage/tmetaimage.cpp
@@ -0,0 +1,98 @@
+
+
+#include <tmetaimage.h>
+
+
+//---------------------------------------------------------
+
+TMetaObject::TMetaObject(const TStringId &typeName):
+  m_handler(),
+  m_data(*this)
+    { setType(typeName); }
+
+//---------------------------------------------------------
+
+TMetaObject::TMetaObject(const std::string &typeName):
+  m_handler(),
+  m_data(*this)
+    { setType(typeName); }
+
+//---------------------------------------------------------
+
+TMetaObject::~TMetaObject()
+  { resetType(); }
+
+//---------------------------------------------------------
+
+void
+TMetaObject::setType(const TStringId &name) {
+  if (m_type != name) {
+    if (m_handler) delete m_handler;
+    m_type = name;
+    Registry::const_iterator i = registry().find(m_type);
+    m_handler = i == registry().end() ? NULL : i->second();
+    onVariantChanged(m_data);
+  }
+}
+
+//---------------------------------------------------------
+
+void
+TMetaObject::onVariantChanged(const TVariant &value)
+  { if (m_handler) m_handler->onDataChanged(value); }
+
+//---------------------------------------------------------
+
+TMetaObject::Registry&
+TMetaObject::registry() {
+  static Registry registry;
+  return registry;
+}
+
+//---------------------------------------------------------
+
+void
+TMetaObject::registerType(const TStringId &typeName, Fabric fabric) {
+  if (registry().count(typeName))
+    std::cerr << "warning: type of TMetaObject are already registered: " << typeName.str() << std::endl;
+  registry()[typeName] = fabric;
+}
+
+//---------------------------------------------------------
+
+void
+TMetaObject::unregisterType(const TStringId &typeName) {
+  if (!registry().count(typeName))
+    std::cerr << "warning: trying to unregister non-registered type of TMetaObject: " << typeName.str() << std::endl;
+  registry().erase(typeName);
+}
+
+//---------------------------------------------------------
+
+TMetaImage::TMetaImage()
+  { }
+
+//---------------------------------------------------------
+
+TMetaImage::TMetaImage(const TMetaImage &other):
+  m_objects(*Reader(other))
+  { }
+
+//---------------------------------------------------------
+
+TMetaImage::~TMetaImage()
+  { }
+
+//---------------------------------------------------------
+
+TImage*
+TMetaImage::cloneImage() const
+  { return new TMetaImage(*this); }
+
+//---------------------------------------------------------
+
+TRectD
+TMetaImage::getBBox() const
+  { return TRectD(); }
+
+//---------------------------------------------------------
diff --git a/toonz/sources/common/tvariant.cpp b/toonz/sources/common/tvariant.cpp
index 6ed5b6d..2559103 100644
--- a/toonz/sources/common/tvariant.cpp
+++ b/toonz/sources/common/tvariant.cpp
@@ -101,7 +101,10 @@ TVariant&
 TVariant::operator[] (const TStringId &field) {
   setType(Map);
   TVariant &result = m_map[field];
-  if (!result.m_parent) result.setParent(*this, field);
+  if (!result.m_parent) {
+    result.setParent(*this, field);
+    touch();
+  }
   return result;
 }
 
diff --git a/toonz/sources/include/tassistantsimage.h b/toonz/sources/include/tassistantsimage.h
deleted file mode 100644
index 2b1c8cc..0000000
--- a/toonz/sources/include/tassistantsimage.h
+++ /dev/null
@@ -1,111 +0,0 @@
-#pragma once
-
-#ifndef TASSISTANTSIMAGE_INCLUDED
-#define TASSISTANTSIMAGE_INCLUDED
-
-#include "timage.h"
-#include "tthreadmessage.h"
-
-#include <QReadLocker>
-#include <QWriteLocker>
-#include <QReadWriteLock>
-
-#include <string>
-
-#undef DVAPI
-#undef DVVAR
-#ifdef TASSISTANTSIMAGE_EXPORTS
-#define DVAPI DV_EXPORT_API
-#define DVVAR DV_EXPORT_VAR
-#else
-#define DVAPI DV_IMPORT_API
-#define DVVAR DV_IMPORT_VAR
-#endif
-
-//-------------------------------------------------------------------
-
-class TAssistantDesc;
-typedef std::vector<TAssistantDesc> TAssistantDescList;
-
-//-------------------------------------------------------------------
-
-class TAssistantDesc {
-public:
-  std::string type;
-  std::vector<TPointD> points;
-  TSmartObjectP handler;
-
-  TPointD& operator[] (int index) {
-    if (index <= (int)points.size()) points.resize(index + 1);
-    return points[index];
-  }
-
-  const TPointD& operator[] (int index) const {
-    static const TPointD blank;
-    return index <= (int)points.size() ? blank : points[index];
-  }
-};
-
-//-------------------------------------------------------------------
-
-//! An image containing an assistants for painting.
-
-class DVAPI TAssistantsImage final : public TImage {
-public:
-  class Reader: public QReadLocker {
-  private:
-    const TAssistantsImage &m_image;
-  public:
-    Reader(const TAssistantsImage &image):
-      QReadLocker(&image.m_rwLock), m_image(image) { }
-    const TAssistantsImage& image() const
-      { return m_image; }
-    const TAssistantDescList& get() const
-      { return m_image.m_assistants; }
-    const TAssistantDescList& operator*() const
-      { return get(); }
-    const TAssistantDescList* operator->() const
-      { return &get(); }
-  };
-
-  class Writer: public QWriteLocker {
-  private:
-    TAssistantsImage &m_image;
-  public:
-    Writer(TAssistantsImage &image):
-      QWriteLocker(&image.m_rwLock), m_image(image) { }
-    TAssistantsImage& image() const
-      { return m_image; }
-    TAssistantDescList& get() const
-      { return m_image.m_assistants; }
-    TAssistantDescList& operator*() const
-      { return get(); }
-    TAssistantDescList* operator->() const
-      { return &get(); }
-  };
-
-private:
-  mutable QReadWriteLock m_rwLock;
-  TAssistantDescList m_assistants;
-
-public:
-  TAssistantsImage();
-  ~TAssistantsImage();
-
-private:
-  //! not implemented
-  TAssistantsImage(const TAssistantsImage &other);
-  TAssistantsImage &operator=(const TAssistantsImage &) { return *this; }
-
-public:
-  //! Return the image type
-  TImage::Type getType() const override { return TImage::ASSISTANTS; }
-
-  //! Return a clone of image
-  TImage* cloneImage() const override;
-
-  //! Return the bbox of the image
-  TRectD getBBox() const override;
-};
-
-#endif
diff --git a/toonz/sources/include/timage.h b/toonz/sources/include/timage.h
index 63aeff0..435f31b 100644
--- a/toonz/sources/include/timage.h
+++ b/toonz/sources/include/timage.h
@@ -50,7 +50,7 @@ lines, curves, and shapes or polygon(s),
     VECTOR       = 2,  //!< A vector image.
     TOONZ_RASTER = 3,  //!< A colormap raster image.
     MESH         = 4,  //!< A textured mesh image.
-    ASSISTANTS   = 5   //!< An assistants for painting image.
+    META         = 5   //!< A non-painting data, GUI helpers, assistants, etc.
   };
 
   /*!
diff --git a/toonz/sources/include/tmetaimage.h b/toonz/sources/include/tmetaimage.h
new file mode 100644
index 0000000..8504441
--- /dev/null
+++ b/toonz/sources/include/tmetaimage.h
@@ -0,0 +1,178 @@
+#pragma once
+
+#ifndef TMETAIMAGE_INCLUDED
+#define TMETAIMAGE_INCLUDED
+
+#include "timage.h"
+#include "tthreadmessage.h"
+#include "tsmartpointer.h"
+#include "tvariant.h"
+
+#include <QReadLocker>
+#include <QWriteLocker>
+#include <QReadWriteLock>
+
+#include <string>
+
+#undef DVAPI
+#undef DVVAR
+#ifdef TMETAIMAGE_EXPORTS
+#define DVAPI DV_EXPORT_API
+#define DVVAR DV_EXPORT_VAR
+#else
+#define DVAPI DV_IMPORT_API
+#define DVVAR DV_IMPORT_VAR
+#endif
+
+//-------------------------------------------------------------------
+
+class TMetaObject;
+class TMetaObjectHandler;
+typedef TSmartPointerT<TMetaObject> TMetaObjectP;
+typedef TSmartRefT<TMetaObject> TMetaObjectR;
+typedef std::vector<TMetaObjectP> TMetaObjectList;
+typedef std::vector<TMetaObjectR> TMetaObjectRefList;
+
+//-------------------------------------------------------------------
+
+class DVAPI TMetaObject: public TSmartObject, public TVariantOwner {
+public:
+  typedef TMetaObjectHandler* (*Fabric)();
+  typedef std::map<TStringId, Fabric> Registry;
+
+  template<typename T>
+  class Registrator {
+  public:
+    typedef T Type;
+    static TMetaObjectHandler* fabric() { return new Type(); }
+    Registrator(const std::string &typeName)
+      { registerType(typeName, fabric); }
+    Registrator(const TStringId &typeName)
+      { registerType(typeName, fabric); }
+  };
+
+private:
+  TStringId m_type;
+  TMetaObjectHandler *m_handler;
+  TVariant m_data;
+
+public:
+  explicit TMetaObject(const TStringId &typeName = TStringId());
+  explicit TMetaObject(const std::string &typeName);
+  ~TMetaObject();
+
+  void setType(const TStringId &name);
+  inline void setType(const std::string &name)
+    { setType(TStringId(name)); }
+  inline void resetType()
+    { setType(TStringId()); }
+  inline const TStringId& getType() const
+    { return m_type; }
+  inline const std::string& getTypeName() const
+    { return m_type.str(); }
+  inline const TVariant& data() const
+    { return m_data; }
+  inline TVariant& data()
+    { return m_data; }
+
+  template<typename T>
+  const T* getHandler() const
+    { return dynamic_cast<T*>(m_handler); }
+  template<typename T>
+  T* getHandler()
+    { return dynamic_cast<T*>(m_handler); }
+
+  void onVariantChanged(const TVariant &value) override;
+
+  static Registry& registry();
+  static void registerType(const TStringId &name, Fabric fabric);
+  static void unregisterType(const TStringId &name);
+  inline static void registerType(const std::string &name, Fabric fabric)
+    { registerType(TStringId(name), fabric); }
+  inline static void unregisterType(const std::string &name)
+    { unregisterType(TStringId::find(name)); }
+};
+
+//-------------------------------------------------------------------
+
+class DVAPI TMetaObjectHandler {
+private:
+  TMetaObject &m_object;
+
+public:
+  TMetaObjectHandler(TMetaObject &object):
+    m_object(object) { }
+  virtual ~TMetaObjectHandler() { }
+
+  inline const TMetaObject& object() const
+    { return m_object; }
+  inline TMetaObject& object()
+    { return m_object; }
+  inline const TVariant& data() const
+    { return object().data(); }
+  inline TVariant& data()
+    { return object().data(); }
+
+  virtual void onDataChanged(const TVariant &value) { }
+  virtual void fixData() { }
+};
+
+//-------------------------------------------------------------------
+
+//! An image containing an assistants for painting.
+
+class DVAPI TMetaImage final : public TImage {
+public:
+  class Reader: public QReadLocker {
+  private:
+    const TMetaImage &m_image;
+  public:
+    Reader(const TMetaImage &image):
+      QReadLocker(&image.m_rwLock), m_image(image) { }
+    const TMetaImage& image() const
+      { return m_image; }
+    const TMetaObjectRefList& get() const
+      { return m_image.m_objects; }
+    const TMetaObjectRefList& operator*() const
+      { return get(); }
+    const TMetaObjectRefList* operator->() const
+      { return &get(); }
+  };
+
+  class Writer: public QWriteLocker {
+  private:
+    TMetaImage &m_image;
+  public:
+    Writer(TMetaImage &image):
+      QWriteLocker(&image.m_rwLock), m_image(image) { }
+    TMetaImage& image() const
+      { return m_image; }
+    TMetaObjectRefList& get() const
+      { return m_image.m_objects; }
+    TMetaObjectRefList& operator*() const
+      { return get(); }
+    TMetaObjectRefList* operator->() const
+      { return &get(); }
+  };
+
+private:
+  mutable QReadWriteLock m_rwLock;
+  TMetaObjectRefList m_objects;
+
+  //! not implemented
+  TMetaImage(const TMetaImage &other);
+  TMetaImage &operator=(const TMetaImage &) { return *this; }
+
+public:
+  TMetaImage();
+  ~TMetaImage();
+
+  //! Return the image type
+  TImage::Type getType() const override { return TImage::META; }
+  //! Return a clone of image
+  TImage* cloneImage() const override;
+  //! Return the bbox of the image
+  TRectD getBBox() const override;
+};
+
+#endif
diff --git a/toonz/sources/include/tools/assistant.h b/toonz/sources/include/tools/assistant.h
index cf09983..a6b7192 100644
--- a/toonz/sources/include/tools/assistant.h
+++ b/toonz/sources/include/tools/assistant.h
@@ -9,9 +9,9 @@
 // TnzLib includes
 
 // TnzCore includes
-#include <tassistantsimage.h>
 #include <tsmartpointer.h>
 #include <tgeometry.h>
+#include <tmetaimage.h>
 
 // std includes
 #include <vector>
@@ -39,7 +39,6 @@ class TAssistant;
 class TGuideline;
 
 typedef TSmartPointerT<TGuideline> TGuidelineP;
-typedef TSmartPointerT<TAssistant> TAssistantP;
 typedef std::vector<TGuidelineP> TGuidelineList;
 
 //===================================================================
@@ -52,15 +51,12 @@ class DVAPI TGuideline final : public TSmartObject {
 public:
   virtual TTrackPoint transformPoint(const TTrackPoint &point) const
     { return point; }
-
   virtual void draw(TToolViewer *viewer, bool active) const
     { }
-
   void draw(TToolViewer *viewer) const
     { draw(viewer, false); }
 
   double calcTrackWeight(const TTrack &track, const TAffine &affine) const;
-
   static TGuidelineP findBest(const TGuidelineList &guidelines, const TTrack &track, const TAffine &affine);
 };
 
@@ -69,40 +65,13 @@ public:
 //    TAssistant definition
 //*****************************************************************************************
 
-class DVAPI TAssistant final : public TSmartObject {
+class DVAPI TAssistant final : public TMetaObjectHandler {
 public:
-  typedef TAssistant* (*Fabric)();
-  typedef std::map<std::string, Fabric> Registry;
-
-  template<typename T>
-  class Registrator {
-  public:
-    typedef T Type;
-    static TAssistant* fabric() { return new Type(); }
-    Registrator(const std::string &name) { getRegistry()[name] = fabric; }
-  };
-
-  static Registry& getRegistry();
-  static TAssistant* create(const std::string &name);
-  static TAssistantP wrap(TAssistantDesc &desc);
-
-  virtual void assign(TAssistantDesc &desc)
-    { }
-  virtual void onMovePoint(
-    TAssistantDesc &desc,
-    int index,
-    const TPointD &position ) { desc[index] = position; }
-  virtual void getGuidelines(
-    const TAssistantDesc &desc,
-    const TPointD &position,
-    TGuidelineList &outGuidelines ) { }
-  virtual void draw(
-    const TAssistantDesc &desc,
-    TToolViewer *viewer ) { }
-  virtual void drawEdit(
-    const TAssistantDesc &desc,
-    TToolViewer *viewer,
-    int currentPointIndex ) { }
+  // TODO: handle data changes
+
+  virtual void getGuidelines(const TPointD &position, TGuidelineList &outGuidelines) { }
+  virtual void draw(TToolViewer *viewer) { }
+  virtual void drawEdit(TToolViewer *viewer, int currentPointIndex) { }
 };
 
 
diff --git a/toonz/sources/include/tvariant.h b/toonz/sources/include/tvariant.h
index a71a96e..4934276 100644
--- a/toonz/sources/include/tvariant.h
+++ b/toonz/sources/include/tvariant.h
@@ -91,7 +91,7 @@ public:
 class DVAPI TVariantOwner {
 public:
   virtual ~TVariantOwner() { }
-  virtual void onTouchVariant(const TVariant &value) { }
+  virtual void onVariantChanged(const TVariant &value) { }
 };
 
 //-------------------------------------------------------------------
@@ -197,7 +197,7 @@ public:
     m_parent() { setMap(v); }
 
   inline void touch()
-    { if (m_root->m_owner) m_root->m_owner->onTouchVariant(*this); }
+    { if (m_root->m_owner) m_root->m_owner->onVariantChanged(*this); }
 
   inline TVariant& operator=(const TVariant &other) {
     switch(other.m_type) {
@@ -322,6 +322,8 @@ public:
     { return m_parent || !m_parentField ? this - &m_parent->m_list.front() : 0; }
   inline const TStringId& parentField() const
     { return m_parentField; }
+  inline bool isRoot() const
+    { return this == m_root; }
 
   int getPathSize() const;
   void getParentPath(TVariantPath &outPath) const;
diff --git a/toonz/sources/tnzcore/CMakeLists.txt b/toonz/sources/tnzcore/CMakeLists.txt
index f9d2cd8..72890ea 100644
--- a/toonz/sources/tnzcore/CMakeLists.txt
+++ b/toonz/sources/tnzcore/CMakeLists.txt
@@ -122,7 +122,7 @@ set(HEADERS ${MOC_HEADERS}
     ../include/tversion.h
     ../include/tstringid.h
     ../include/tvariant.h
-    ../include/tassistantsimage.h
+    ../include/tmetaimage.h
 )
 
 set(SOURCES
@@ -245,7 +245,7 @@ set(SOURCES
     ../common/tmeshimage/tmeshimage.cpp
     ../common/tmsgcore.cpp
     ../common/tvrender/tfont_qt.cpp
-    ../common/tassistantsimage/tassistantsimage.cpp
+    ../common/tmetaimage/tmetaimage.cpp
     ../common/tstringid.cpp
     ../common/tvariant.cpp
 )
@@ -288,7 +288,7 @@ add_definitions(
     -DTSOUND_EXPORTS
     -DTIMAGE_IO_EXPORTS
     -DTRASTERIMAGE_EXPORTS
-    -DTASSISTANTSIMAGE_EXPORTS
+    -DTMETAIMAGE_EXPORTS
     -DTVRENDER_EXPORTS
     -DTFLASH_EXPORTS
     -DTROP_EXPORTS
diff --git a/toonz/sources/tnztools/assistant.cpp b/toonz/sources/tnztools/assistant.cpp
index 44077b7..80510cd 100644
--- a/toonz/sources/tnztools/assistant.cpp
+++ b/toonz/sources/tnztools/assistant.cpp
@@ -63,28 +63,4 @@ TGuideline::findBest(const TGuidelineList &guidelines, const TTrack &track, cons
 //    TAssistant implementation
 //************************************************************************
 
-TAssistant::Registry&
-TAssistant::getRegistry() {
-  static Registry registry;
-  return registry;
-}
-
-//---------------------------------------------------------------------------------------------------
-
-TAssistant*
-TAssistant::create(const std::string &name) {
-  const Registry &registry = getRegistry();
-  Registry::const_iterator i = registry.find(name);
-  return i == registry.end() ? NULL : i->second();
-}
-
-//---------------------------------------------------------------------------------------------------
-
-TAssistantP
-TAssistant::wrap(TAssistantDesc &desc) {
-  if (desc.handler)
-    return dynamic_cast<TAssistant*>(desc.handler.getPointer());
-  TAssistantP assistant = create(desc.type);
-  desc.handler = assistant.getPointer();
-  return assistant;
-}
+// TODO: