diff --git a/toonz/sources/common/tstringid.cpp b/toonz/sources/common/tstringid.cpp
new file mode 100644
index 0000000..4131b76
--- /dev/null
+++ b/toonz/sources/common/tstringid.cpp
@@ -0,0 +1,72 @@
+
+
+#include <tstringid.h>
+
+#include <QMutex>
+#include <QMutexLocker>
+
+//---------------------------------------------------------
+
+struct TStringId::StaticData {
+  Map map;
+  std::vector<Iterator> iterators;
+  Iterator none;
+  QMutex mutex;
+  StaticData() {
+    map[std::string()] = 0;
+    none = map.begin();
+    iterators.push_back(none);
+  }
+
+  static StaticData& instance() {
+    static StaticData data;
+    return data;
+  }
+};
+
+//---------------------------------------------------------
+
+const TStringId::Iterator&
+TStringId::none()
+  { return StaticData::instance().none; }
+
+//---------------------------------------------------------
+
+TStringId::Iterator
+TStringId::genIter(const std::string &str) {
+  StaticData &data = StaticData::instance();
+  if (str.empty()) return data.none;
+
+  QMutexLocker lock(&data.mutex);
+  Iterator i = data.map.find(str);
+  if (i == data.map.end()) {
+    i = data.map.insert(std::pair<std::string, int>(str, (int)data.iterators.size())).first;
+    data.iterators.push_back(i);
+  }
+  return i;
+}
+
+//---------------------------------------------------------
+
+TStringId::Iterator
+TStringId::findIter(int id) {
+  StaticData &data = StaticData::instance();
+  if (id <= 0) return data.none;
+
+  QMutexLocker lock(&data.mutex);
+  return id < (int)data.iterators.size() ? data.iterators[id] : data.none;
+}
+
+//---------------------------------------------------------
+
+TStringId::Iterator
+TStringId::findIter(const std::string &str) {
+  StaticData &data = StaticData::instance();
+  if (str.empty()) return data.none;
+
+  QMutexLocker lock(&data.mutex);
+  Iterator i = data.map.find(str);
+  return i == data.map.end() ? data.none : i;
+}
+
+//---------------------------------------------------------
diff --git a/toonz/sources/common/tvariant.cpp b/toonz/sources/common/tvariant.cpp
new file mode 100644
index 0000000..6ed5b6d
--- /dev/null
+++ b/toonz/sources/common/tvariant.cpp
@@ -0,0 +1,195 @@
+
+
+#include <tvariant.h>
+
+//---------------------------------------------------------
+
+int
+TVariantPath::compare(
+  const TVariantPath &a, int beginA,
+  const TVariantPath &b, int beginB, int count )
+{
+  assert(beginA >= 0 && beginA <= (int)a.size());
+  assert(beginB >= 0 && beginB <= (int)b.size());
+  if (count == 0) return 0;
+  int countA = std::min(count, (int)a.size() - beginA);
+  int countB = std::min(count, (int)b.size() - beginB);
+  count = std::min(countA, countB);
+
+  TVariantPath::const_iterator ia = a.begin() + beginA;
+  TVariantPath::const_iterator ib = b.begin() + beginB;
+  for(int i = 0; i < count; ++i, ++ia, ++ib)
+    if ((*ia) < (*ib)) return -1; else
+      if ((*ib) < (*ia)) return 1;
+  return countA < countB ? -1
+       : countB < countA ?  1 : 0;
+}
+
+//---------------------------------------------------------
+
+void
+TVariant::setParentForChilds() {
+  if (m_type == List) {
+    for(TVariantList::iterator i = m_list.begin(); i != m_list.end(); ++i)
+      i->setParent(*this);
+  } else
+  if (m_type == Map) {
+    for(TVariantMap::iterator i = m_map.begin(); i != m_map.end(); ++i)
+      i->second.setParent(*this, i->first);
+  }
+}
+
+//---------------------------------------------------------
+
+const TVariant&
+TVariant::blank() {
+  static const TVariant blank;
+  return blank;
+}
+
+//---------------------------------------------------------
+
+void
+TVariant::resize(int size) {
+  setType(List);
+  int prevSize = (int)m_list.size();
+  if (prevSize == size) return;
+  m_list.resize(size);
+  if (prevSize < size)
+    for(TVariantList::iterator i = m_list.begin() + prevSize; i != m_list.end(); ++i)
+      i->setParent(*this);
+  touch();
+}
+
+//---------------------------------------------------------
+
+void
+TVariant::insert(int index, const TVariant &v) {
+  resize(std::max((int)m_list.size(), index));
+  m_list.insert(m_list.begin() + index, v);
+  m_list[index].setParent(*this);
+  touch();
+}
+
+//---------------------------------------------------------
+
+void
+TVariant::remove(int index) {
+  if (m_type == List && index >= 0 && index < (int)m_list.size())
+    { m_list.erase(m_list.begin() + index); touch(); }
+}
+
+//---------------------------------------------------------
+
+TVariant&
+TVariant::operator[] (int index) {
+  setType(List);
+  assert(index >= 0);
+  int prevSize = (int)m_list.size();
+  if (index >= prevSize) {
+    m_list.resize(index + 1);
+    for(TVariantList::iterator i = m_list.begin() + prevSize; i != m_list.end(); ++i)
+      i->setParent(*this);
+    touch();
+  }
+  return m_list[index];
+}
+
+//---------------------------------------------------------
+
+TVariant&
+TVariant::operator[] (const TStringId &field) {
+  setType(Map);
+  TVariant &result = m_map[field];
+  if (!result.m_parent) result.setParent(*this, field);
+  return result;
+}
+
+//---------------------------------------------------------
+
+bool
+TVariant::remove(const TStringId &field) {
+  if (m_type == Map && m_map.erase(field))
+    { touch(); return true; }
+  return false;
+}
+
+//---------------------------------------------------------
+
+const TVariant&
+TVariant::byPath(const TVariantPath &path, int begin, int end) const {
+  if ((int)path.size() <= begin || begin >= end) return *this;
+  if (isNone()) return blank();
+  return byPath(path[begin]).byPath(path, begin + 1, end);
+}
+
+//---------------------------------------------------------
+
+TVariant&
+TVariant::byPath(const TVariantPath &path, int begin, int end) {
+  if ((int)path.size() <= begin || begin >= end) return *this;
+  return byPath(path[begin]).byPath(path, begin + 1, end);
+}
+
+//---------------------------------------------------------
+
+int
+TVariant::getPathSize() const {
+  const TVariant *a = this->m_parent;
+  int ac = 0;
+  while(a) a = a->m_parent, ++ac;
+  return ac;
+}
+
+//---------------------------------------------------------
+
+void
+TVariant::getParentPath(TVariantPath &outPath) const {
+  if (m_parent) {
+    m_parent->getParentPath(outPath);
+    outPath.push_back(parentPathEntry());
+  } else {
+    outPath.clear();
+  }
+}
+
+//---------------------------------------------------------
+
+bool
+TVariant::isChildOf(const TVariant &other) const {
+  for(const TVariant *a = this->m_parent; a; a = a->m_parent)
+    if (a == &other) return true;
+  return false;
+}
+
+//---------------------------------------------------------
+
+bool
+TVariant::isChildOrEqual(const TVariant &other) const {
+  for(const TVariant *a = this; a; a = a->m_parent)
+    if (a == &other) return true;
+  return false;
+}
+
+//---------------------------------------------------------
+
+const TVariant*
+TVariant::findCommonParent(const TVariant &other) const {
+  if (m_root != other.m_root) return NULL;
+  const TVariant *a = this, *b = &other;
+  int ac = 0, bc = 0;
+  while(a->m_parent) a = a->m_parent, ++ac;
+  while(b->m_parent) b = b->m_parent, ++bc;
+
+  a = this, b = &other;
+  while(ac > bc) a = a->m_parent, --ac;
+  while(bc > ac) b = b->m_parent, --bc;
+
+  while(true) {
+    if (a == b) return a;
+    if (ac == 0) break;
+    --ac, a = a->m_parent, b = b->m_parent;
+  }
+
+  return NULL;
+}
diff --git a/toonz/sources/include/tstringid.h b/toonz/sources/include/tstringid.h
new file mode 100644
index 0000000..80f3ca4
--- /dev/null
+++ b/toonz/sources/include/tstringid.h
@@ -0,0 +1,71 @@
+#pragma once
+
+#ifndef TSTRINGID_INCLUDED
+#define TSTRINGID_INCLUDED
+
+#include <tcommon.h>
+
+#include <string>
+#include <vector>
+#include <map>
+
+#undef DVAPI
+#undef DVVAR
+#ifdef TSTRINGID_EXPORTS
+#define DVAPI DV_EXPORT_API
+#define DVVAR DV_EXPORT_VAR
+#else
+#define DVAPI DV_IMPORT_API
+#define DVVAR DV_IMPORT_VAR
+#endif
+
+//-------------------------------------------------------------------
+
+class DVAPI TStringId {
+public:
+  typedef std::map<std::string, int> Map;
+  typedef Map::iterator Iterator;
+
+  struct StaticData;
+  static const Iterator& none();
+  static Iterator genIter(const std::string &str);
+  static Iterator findIter(int id);
+  static Iterator findIter(const std::string &str);
+
+private:
+  Iterator iter;
+
+  inline explicit TStringId(const Iterator &iter):
+    iter(iter) { }
+
+public:
+  inline TStringId():
+    iter(none()) { }
+  inline explicit TStringId(const std::string &str):
+    iter(genIter(str)) { }
+  inline void reset()
+    { iter = none(); }
+  inline void set(const std::string &str)
+    { if (iter->first != str) iter = genIter(str); }
+
+  inline int id() const
+    { return iter->second; }
+  inline const std::string& str() const
+    { return iter->first; }
+
+  inline operator bool () const
+    { return (bool)id(); }
+  inline bool operator== (const TStringId &other) const
+    { return id() == other.id(); }
+  inline bool operator!= (const TStringId &other) const
+    { return id() != other.id(); }
+  inline bool operator< (const TStringId &other) const
+    { return id() < other.id(); }
+
+  inline static TStringId find(int id)
+    { return TStringId(findIter(id)); }
+  inline static TStringId find(const std::string &str)
+    { return TStringId(findIter(str)); }
+};
+
+#endif
diff --git a/toonz/sources/include/tvariant.h b/toonz/sources/include/tvariant.h
new file mode 100644
index 0000000..a71a96e
--- /dev/null
+++ b/toonz/sources/include/tvariant.h
@@ -0,0 +1,351 @@
+#pragma once
+
+#ifndef TVARIANT_INCLUDED
+#define TVARIANT_INCLUDED
+
+#include <tcommon.h>
+#include <tstringid.h>
+
+#include <string>
+#include <vector>
+#include <map>
+
+#undef DVAPI
+#undef DVVAR
+#ifdef TVARIANT_EXPORTS
+#define DVAPI DV_EXPORT_API
+#define DVVAR DV_EXPORT_VAR
+#else
+#define DVAPI DV_IMPORT_API
+#define DVVAR DV_IMPORT_VAR
+#endif
+
+//-------------------------------------------------------------------
+
+class TVariant;
+typedef std::vector<TVariant> TVariantList;
+typedef std::map<TStringId, TVariant> TVariantMap;
+
+//-------------------------------------------------------------------
+
+class DVAPI TVariantPathEntry {
+private:
+  int m_index;
+  TStringId m_field;
+public:
+  inline explicit TVariantPathEntry(int index = -1):
+    m_index(index) { }
+  inline explicit TVariantPathEntry(const TStringId &field):
+    m_index(-1), m_field(field) { }
+  inline explicit TVariantPathEntry(const std::string &fieldName):
+    m_index(-1), m_field(fieldName) { }
+
+  inline bool isIndex() const
+    { return m_index >= 0; }
+  inline bool isField() const
+    { return !isIndex(); }
+  inline int index() const
+    { return m_index; }
+  inline int field() const
+    { return m_field; }
+
+  inline bool operator== (const TVariantPathEntry &other) const
+    { return m_index == other.m_index && m_field == other.m_field; }
+  inline bool operator!= (const TVariantPathEntry &other) const
+    { return m_index != other.m_index || m_field != other.m_field; }
+  inline bool operator< (const TVariantPathEntry &other) const
+    { return m_index < other.m_index || m_field < other.m_field; }
+
+  inline void set(int index)
+    { m_index = index; m_field.reset(); }
+  inline void set(const TStringId &field)
+    { m_index = 0; m_field = field; }
+  inline void set(const std::string &fieldName)
+    { m_index = 0; m_field.set(fieldName); }
+};
+
+//-------------------------------------------------------------------
+
+class DVAPI TVariantPath: public std::vector<TVariantPathEntry> {
+public:
+  inline bool isSubPathOf(const TVariantPath &other) const
+    { return compare(*this, 0, other, 0, (int)size()); }
+  inline bool isBasePathOf(const TVariantPath &other) const
+    { return other.isSubPathOf(*this); }
+  inline int compare(const TVariantPath &other) const
+    { return compare(*this, 0, other, 0, (int)std::max(size(), other.size())); }
+  inline bool operator==(const TVariantPath &other) const
+    { return compare(other) == 0; }
+  inline bool operator!=(const TVariantPath &other) const
+    { return compare(other) != 0; }
+  inline bool operator<(const TVariantPath &other) const
+    { return compare(other) < 0; }
+
+  static int compare(
+    const TVariantPath &a, int beginA,
+    const TVariantPath &b, int beginB, int count );
+};
+
+//-------------------------------------------------------------------
+
+class DVAPI TVariantOwner {
+public:
+  virtual ~TVariantOwner() { }
+  virtual void onTouchVariant(const TVariant &value) { }
+};
+
+//-------------------------------------------------------------------
+
+class DVAPI TVariant {
+public:
+  enum Type {
+    None,
+    Bool,
+    Double,
+    String,
+    List,
+    Map
+  };
+
+private:
+  Type         m_type;
+  bool         m_bool;
+  double       m_double;
+  std::string  m_string;
+  TVariantList m_list;
+  TVariantMap  m_map;
+
+  TVariantOwner* m_owner;
+  TVariant *m_root;
+  TVariant *m_parent;
+  TStringId m_parentField;
+
+  void setParentForChilds();
+  inline void setParent(TVariant &parent, const TStringId &parentField = TStringId()) {
+    m_root = parent.m_root;
+    m_parent = &parent;
+    m_parentField = parentField;
+    setParentForChilds();
+  }
+
+public:
+  static const TVariant& blank();
+
+  inline TVariant():
+    m_type(None),
+    m_bool(),
+    m_double(),
+    m_owner(),
+    m_root(this),
+    m_parent() { }
+  inline TVariant(const TVariant &other):
+    m_type(None),
+    m_bool(),
+    m_double(),
+    m_owner(),
+    m_root(this),
+    m_parent() { *this = other; }
+  inline explicit TVariant(TVariantOwner &owner, const TVariant &v = TVariant()):
+    m_type(None),
+    m_bool(),
+    m_double(),
+    m_owner(),
+    m_root(this),
+    m_parent() { *this = v; m_owner = &owner; }
+  inline explicit TVariant(Type t):
+    m_type(t),
+    m_bool(),
+    m_double(),
+    m_owner(),
+    m_root(this),
+    m_parent() { }
+  inline explicit TVariant(bool v):
+    m_type(Bool),
+    m_bool(v),
+    m_double(),
+    m_owner(),
+    m_root(this),
+    m_parent() { }
+  inline explicit TVariant(double v):
+    m_type(Double),
+    m_bool(),
+    m_double(v),
+    m_owner(),
+    m_root(this),
+    m_parent() { }
+  inline explicit TVariant(const std::string &v):
+    m_type(String),
+    m_bool(),
+    m_double(),
+    m_string(v),
+    m_owner(),
+    m_root(this),
+    m_parent() { }
+  inline explicit TVariant(const TVariantList &v):
+    m_type(None),
+    m_bool(),
+    m_double(),
+    m_owner(),
+    m_root(this),
+    m_parent() { setList(v); }
+  inline explicit TVariant(const TVariantMap &v):
+    m_type(None),
+    m_bool(),
+    m_double(),
+    m_owner(),
+    m_root(this),
+    m_parent() { setMap(v); }
+
+  inline void touch()
+    { if (m_root->m_owner) m_root->m_owner->onTouchVariant(*this); }
+
+  inline TVariant& operator=(const TVariant &other) {
+    switch(other.m_type) {
+    case Bool   : setBool(other.m_bool); break;
+    case Double : setDouble(other.m_double); break;
+    case String : setString(other.m_string); break;
+    case List   : setList(other.m_list); break;
+    case Map    : setMap(other.m_map); break;
+    default     : reset(); break;
+    }
+    return *this;
+  }
+
+  inline void clear() {
+    m_bool = bool();
+    m_double = double();
+    m_string.clear();
+    m_list.clear();
+    m_map.clear();
+    touch();
+  }
+  inline void setType(Type t)
+    { if (m_type != t) { m_type = t; clear(); } }
+  inline void reset()
+    { setType(None); }
+
+  inline Type getType() const
+    { return m_type; }
+  inline bool isNone() const
+    { return m_type == None; }
+  inline bool getBool() const
+    { return m_bool; }
+  inline double getDouble() const
+    { return m_double; }
+  inline const std::string& getString() const
+    { return m_string; }
+  inline const TVariantList& getList() const
+    { return m_list; }
+  inline const TVariantMap& getMap() const
+    { return m_map; }
+
+  inline void setNone()
+    { reset(); }
+  inline void setBool(bool v)
+    { setType(Bool); m_bool = v; touch(); }
+  inline void setDouble(double v)
+    { setType(Double); m_double = v; touch(); }
+  inline void setString(const std::string &v)
+    { setType(String); m_string = v; touch(); }
+  inline void setList(const TVariantList &v)
+    { setType(List); m_list = v; setParentForChilds(); touch(); }
+  inline void setMap(const TVariantMap &v)
+    { setType(Map); m_map = v; setParentForChilds(); touch(); }
+
+  // list methods
+  void resize(int size);
+  void insert(int index, const TVariant &v);
+  void remove(int index);
+  TVariant& operator[] (int index);
+  inline int size() const
+    { return (int)(m_type == List ? m_list.size() : m_map.size()); }
+  inline void clearList()
+    { resize(0); }
+  inline void append(const TVariant &v)
+    { insert((int)m_list.size(), v); }
+  inline const TVariant& operator[] (int index) const {
+    assert(index >= 0);
+    return index < (int)m_list.size() ? m_list[index] : blank();
+  }
+
+  // map methods
+  TVariant& operator[] (const TStringId &field);
+  bool remove(const TStringId &field);
+  inline bool contains(const TStringId &field) const
+    { return m_type == Map && m_map.count(field); }
+  inline const TVariant& operator[] (const TStringId &field) const {
+    TVariantMap::const_iterator i = m_map.find(field);
+    return i == m_map.end() ? blank() : i->second;
+  }
+  inline bool contains(const std::string &field) const
+    { return contains(TStringId::find(field)); }
+  inline const TVariant& operator[] (const std::string &field) const
+    { return (*this)[TStringId::find(field)]; }
+  inline TVariant& operator[] (const std::string &field)
+    { return (*this)[TStringId::find(field)]; }
+  inline void remove(const std::string &field)
+    { remove(TStringId::find(field)); }
+
+  // path methods
+  const TVariant& byPath(const TVariantPath &path, int begin, int end) const;
+  TVariant& byPath(const TVariantPath &path, int begin, int end);
+  inline const TVariant& byPath(const TVariantPathEntry &entry) const {
+    return entry.isIndex()
+         ? (m_type == List ? (*this)[entry.index()] : blank())
+         : (m_type == Map  ? (*this)[entry.field()] : blank());
+  }
+  inline TVariant& byPath(const TVariantPathEntry &entry)
+    { return entry.isIndex() ? (*this)[entry.index()] : (*this)[entry.field()]; }
+  inline const TVariant& byPath(const TVariantPath &path, int begin = 0) const
+    { return byPath(path, begin, (int)path.size()); }
+  inline TVariant& byPath(const TVariantPath &path, int begin = 0)
+    { return byPath(path, begin, (int)path.size()); }
+  inline const TVariant& operator[] (const TVariantPath &path) const
+    { return byPath(path); }
+  inline TVariant& operator[] (const TVariantPath &path)
+    { return byPath(path); }
+
+  // hierarchy methods
+  inline const TVariantOwner* owner() const
+    { return m_root->m_owner; }
+  inline TVariantOwner* owner()
+    { return m_root->m_owner; }
+  inline const TVariant& root() const
+    { return *m_root; }
+  inline TVariant& root()
+    { return *m_root; }
+  inline const TVariant* parent() const
+    { return m_parent; }
+  inline TVariant* parent()
+    { return m_parent; }
+  inline int parentIndex() const
+    { return m_parent || !m_parentField ? this - &m_parent->m_list.front() : 0; }
+  inline const TStringId& parentField() const
+    { return m_parentField; }
+
+  int getPathSize() const;
+  void getParentPath(TVariantPath &outPath) const;
+  inline TVariantPathEntry parentPathEntry() const {
+    return !m_parent               ? TVariantPathEntry()
+         : m_parent->m_type == Map ? TVariantPathEntry(m_parentField)
+         : TVariantPathEntry( this - &m_parent->m_list.front() );
+  }
+  inline TVariantPath getParentPath() const
+    { TVariantPath path; getParentPath(path); return path; }
+
+  bool isChildOf(const TVariant &other) const;
+  bool isChildOrEqual(const TVariant &other) const;
+  inline bool isParentOf(const TVariant &other) const
+    { return other.isChildOf(*this); }
+  inline bool isParentOrEqual(const TVariant &other) const
+    { return other.isChildOrEqual(*this); }
+  inline bool isChildOrParent(const TVariant &other) const
+    { return isChildOrEqual(other) || isParentOrEqual(other); }
+
+  const TVariant* findCommonParent(const TVariant &other) const;
+
+  // serialization
+  // TODO:
+};
+
+#endif
diff --git a/toonz/sources/tnzcore/CMakeLists.txt b/toonz/sources/tnzcore/CMakeLists.txt
index 0ee1d74..f9d2cd8 100644
--- a/toonz/sources/tnzcore/CMakeLists.txt
+++ b/toonz/sources/tnzcore/CMakeLists.txt
@@ -120,6 +120,8 @@ set(HEADERS ${MOC_HEADERS}
     ../include/tmeshimage.h
     ../include/tgldisplaylistsmanager.h
     ../include/tversion.h
+    ../include/tstringid.h
+    ../include/tvariant.h
     ../include/tassistantsimage.h
 )
 
@@ -244,6 +246,8 @@ set(SOURCES
     ../common/tmsgcore.cpp
     ../common/tvrender/tfont_qt.cpp
     ../common/tassistantsimage/tassistantsimage.cpp
+    ../common/tstringid.cpp
+    ../common/tvariant.cpp
 )
 
 if(BUILD_TARGET_WIN)
@@ -289,6 +293,8 @@ add_definitions(
     -DTFLASH_EXPORTS
     -DTROP_EXPORTS
     -DTSTREAM_EXPORTS
+    -DTSTRINGID_EXPORTS
+    -DTVARIANT_EXPORTS
 )
 
 if(BUILD_TARGET_WIN)