diff --git a/CHANGELOG b/CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,14 @@
 HsQML - Release History
 
+release-0.3.2.0 - 2014.11.13
+
+  * Added OpenGL canvas support.
+  * Added weak references and object finalisers.
+  * Added FactoryPool abstraction.
+  * Added To-only custom marshallers.
+  * Added Ignored type.
+  * Relaxed Cabal dependency constraint on 'text'.
+
 release-0.3.1.1 - 2014.07.31
 
   * Fixed crash when storing Haskell objects in QML variants.
diff --git a/cbits/Canvas.cpp b/cbits/Canvas.cpp
--- a/cbits/Canvas.cpp
+++ b/cbits/Canvas.cpp
@@ -1,29 +1,325 @@
 #include "Canvas.h"
-#include "CanvasImpl.h"
+#include "Manager.h"
 
-HsQMLGLDelegate::HsQMLGLDelegate(
-    HsQMLGLPaintCb paintCb)
-    : mImpl(new HsQMLGLDelegateImpl(paintCb))
+#include <QtCore/qmath.h>
+#include <QtQuick/QSGSimpleTextureNode>
+#include <QtQuick/QSGTexture>
+#include <QtQuick/QSGTransformNode>
+#include <QtQuick/QQuickWindow>
+
+HsQMLGLCallbacks::HsQMLGLCallbacks(
+    HsQMLGLSetupCb setupCb, HsQMLGLCleanupCb cleanupCb,
+    HsQMLGLSyncCb syncCb, HsQMLGLPaintCb paintCb)
+    : mSetupCb(setupCb)
+    , mCleanupCb(cleanupCb)
+    , mSyncCb(syncCb)
+    , mPaintCb(paintCb)
+{}
+
+HsQMLGLCallbacks::~HsQMLGLCallbacks()
 {
+    gManager->freeFun(reinterpret_cast<HsFunPtr>(mSyncCb));
+    gManager->freeFun(reinterpret_cast<HsFunPtr>(mPaintCb));
 }
 
+HsQMLGLDelegateImpl::HsQMLGLDelegateImpl(HsQMLGLMakeCallbacksCb makeContextCb)
+    : mMakeCallbacksCb(makeContextCb)
+{}
+
+HsQMLGLDelegateImpl::~HsQMLGLDelegateImpl()
+{
+    gManager->freeFun(reinterpret_cast<HsFunPtr>(mMakeCallbacksCb));
+}
+
+HsQMLGLDelegate::HsQMLGLDelegate()
+{
+}
+
 HsQMLGLDelegate::~HsQMLGLDelegate()
 {
 }
 
+void HsQMLGLDelegate::setup(HsQMLGLMakeCallbacksCb makeContextCb)
+{
+    mImpl = new HsQMLGLDelegateImpl(makeContextCb);
+}
+
+HsQMLGLDelegate::CallbacksRef HsQMLGLDelegate::makeCallbacks()
+{
+    CallbacksRef dataPtr;
+    if (mImpl) {
+        HsQMLGLSetupCb setupCb;
+        HsQMLGLCleanupCb cleanupCb;
+        HsQMLGLSyncCb syncCb;
+        HsQMLGLPaintCb paintCb;
+        mImpl->mMakeCallbacksCb(&setupCb, &cleanupCb, &syncCb, &paintCb);
+        dataPtr = new HsQMLGLCallbacks(setupCb, cleanupCb, syncCb, paintCb);
+    }
+    return dataPtr;
+}
+
+HsQMLWindowInfoImpl::HsQMLWindowInfoImpl(QQuickWindow* win)
+    : mWin(win)
+    , mBelowCount(0)
+    , mBelowClear(false)
+{}
+
+HsQMLWindowInfo::HsQMLWindowInfo()
+{}
+
+HsQMLWindowInfo HsQMLWindowInfo::getWindowInfo(QQuickWindow* win)
+{
+    const char* propName = "_hsqml_window_info";
+    HsQMLWindowInfo info = win->property(propName).value<HsQMLWindowInfo>();
+    if (!info.mImpl) {
+        info.mImpl = new HsQMLWindowInfoImpl(win);
+        win->setProperty(propName, QVariant::fromValue(info));
+    }
+    return info;
+}
+
+void HsQMLWindowInfo::addBelow()
+{
+    Q_ASSERT(mImpl);
+    if (0 == mImpl->mBelowCount++) {
+        mImpl->mWin->setClearBeforeRendering(false);
+    }
+}
+
+void HsQMLWindowInfo::removeBelow()
+{
+    Q_ASSERT(mImpl);
+    if (0 == --mImpl->mBelowCount) {
+        mImpl->mWin->setClearBeforeRendering(true);
+    }
+}
+
+bool HsQMLWindowInfo::needsBelowClear()
+{
+    Q_ASSERT(mImpl);
+    if (mImpl->mBelowCount && !mImpl->mBelowClear) {
+        mImpl->mBelowClear = true;
+        return true;
+    }
+    return false;
+}
+
+void HsQMLWindowInfo::endFrame()
+{
+    Q_ASSERT(mImpl);
+    mImpl->mBelowClear = false;
+}
+
+HsQMLCanvasBackEnd::HsQMLCanvasBackEnd(
+    QQuickWindow* win,
+    const HsQMLGLDelegate::CallbacksRef& cbs,
+    HsQMLCanvas::DisplayMode mode)
+    : mWindow(win)
+    , mWinInfo(HsQMLWindowInfo::getWindowInfo(win))
+    , mGLCallbacks(cbs)
+    , mGL(NULL)
+    , mStatus(HsQMLCanvas::Okay)
+    , mDisplayMode(mode)
+    , mItemWidth(0)
+    , mItemHeight(0)
+    , mTransformNode(NULL)
+    , mCanvasWidth(0)
+    , mCanvasHeight(0)
+{
+    if (HsQMLCanvas::Above == mDisplayMode) {
+        QObject::connect(
+            win, SIGNAL(afterRendering()),
+            this, SLOT(doRendering()));
+    }
+    else {
+        QObject::connect(
+            win, SIGNAL(beforeRendering()),
+            this, SLOT(doRendering()));
+
+        if (HsQMLCanvas::Below == mDisplayMode) {
+            mWinInfo.addBelow();
+            QObject::connect(
+                win, SIGNAL(frameSwapped()),
+                this, SLOT(doEndFrame()));
+        }
+    }
+}
+
+HsQMLCanvasBackEnd::~HsQMLCanvasBackEnd()
+{
+    if (HsQMLCanvas::Below == mDisplayMode) {
+        mWinInfo.removeBelow();
+    }
+}
+
+void HsQMLCanvasBackEnd::setTransformNode(
+    QSGTransformNode* tn, qreal w, qreal h)
+{
+    mTransformNode = tn;
+    mItemWidth = w;
+    mItemHeight = h;
+}
+
+QSGTexture* HsQMLCanvasBackEnd::updateFBO(qreal w, qreal h)
+{
+    if (HsQMLCanvas::Inline == mDisplayMode) {
+        if (!mFBO || w != mCanvasWidth || h != mCanvasHeight) {
+            mCanvasWidth = w;
+            mCanvasHeight = h;
+            QSize dims(qCeil(mCanvasWidth), qCeil(mCanvasHeight));
+            mFBO.reset(new QOpenGLFramebufferObject(
+                dims, QOpenGLFramebufferObject::Depth));
+            mTexture.reset(mWindow->createTextureFromId(
+                mFBO->texture(), dims, QQuickWindow::TextureHasAlphaChannel));
+        }
+    }
+    else {
+        mTexture.reset();
+        mFBO.reset();
+    }
+    return mTexture.data();
+}
+
+HsQMLCanvas::Status HsQMLCanvasBackEnd::status() const
+{
+    return mStatus;
+}
+
+void HsQMLCanvasBackEnd::setStatus(HsQMLCanvas::Status status)
+{
+    bool change = mStatus != status;
+    mStatus = status;
+    if (change) {
+        statusChanged(mStatus);
+    }
+}
+
+void HsQMLCanvasBackEnd::doRendering()
+{
+    if (!mGL) {
+        mGL = mWindow->openglContext();
+        QObject::connect(
+            mGL, SIGNAL(aboutToBeDestroyed()), this, SLOT(doCleanup()));
+        HsQMLGLCanvasType ctype;
+        QSurfaceFormat format = mGL->format();
+        switch (format.renderableType()) {
+            case QSurfaceFormat::OpenGL: ctype = HSQML_GL_DESKTOP; break;
+            case QSurfaceFormat::OpenGLES: ctype = HSQML_GL_ES; break;
+            default: setStatus(HsQMLCanvas::BadConfig); return;
+        }
+        mGLViewportFn = reinterpret_cast<GLViewportFn>(
+            mGL->getProcAddress("glViewport"));
+        mGLClearColorFn = reinterpret_cast<GLClearColorFn>(
+            mGL->getProcAddress("glClearColor"));
+        mGLClearFn = reinterpret_cast<GLClearFn>(
+            mGL->getProcAddress("glClear"));
+        if (!mGLViewportFn || !mGLClearColorFn || !mGLClearFn) {
+            setStatus(HsQMLCanvas::BadProcs);
+            return;
+        }
+        mGLCallbacks->mSetupCb(
+            ctype, format.majorVersion(), format.minorVersion());
+    }
+
+    // Reset OpenGL state before rendering
+#if QT_VERSION >= 0x050200
+    mWindow->resetOpenGLState();
+#else
+#warning Resetting OpenGL state requires Qt 5.2 or later
+#endif
+
+    // Clear window if painting below the scenegraph
+    if (mWinInfo.needsBelowClear()) {
+        QColor bg = mWindow->color();
+        mGLClearColorFn(bg.redF(), bg.greenF(), bg.blueF(), bg.alphaF());
+        mGLClearFn(GL_COLOR_BUFFER_BIT);
+    }
+
+    // Setup prior to paint callback
+    QMatrix4x4 matrix;
+    bool inlineMode = HsQMLCanvas::Inline == mDisplayMode;
+    if (inlineMode) {
+        if (!mFBO->bind()) {
+            setStatus(HsQMLCanvas::BadBind);
+            return;
+        }
+        mGLViewportFn(0, 0, qCeil(mCanvasWidth), qCeil(mCanvasHeight));
+
+        // Clear FBO to transparent
+        mGLClearColorFn(0, 0, 0, 0);
+        mGLClearFn(GL_COLOR_BUFFER_BIT);
+    }
+    else {
+        // Calculate matrix for non-inline display modes
+        QMatrix4x4 smatrix;
+        QSGNode* node = mTransformNode;
+        while (node) {
+            if (QSGNode::TransformNodeType == node->type()) {
+                QSGTransformNode* tnode = static_cast<QSGTransformNode*>(node);
+                smatrix = tnode->matrix() * smatrix;
+            }
+            node = node->parent();
+        }
+        matrix.translate(-1, 1);
+        matrix.scale(2.0f/mWindow->width(), -2.0f/mWindow->height());
+        matrix *= smatrix;
+        matrix.scale(mItemWidth/2.0f, mItemHeight/2.0f);
+        matrix.translate(1, 1);
+
+        mGLViewportFn(0, 0, mWindow->width(), mWindow->height());
+    }
+
+    setStatus(HsQMLCanvas::Okay);
+    mGLCallbacks->mPaintCb(matrix.data(), mItemWidth, mItemHeight);
+
+    if (inlineMode) {
+        mFBO->release();
+    }
+}
+
+void HsQMLCanvasBackEnd::doEndFrame()
+{
+    mWinInfo.endFrame();
+}
+
+void HsQMLCanvasBackEnd::doCleanup()
+{
+    if (mGL) {
+        mGL->makeCurrent(mWindow);
+        mGL = NULL;
+
+        mTexture.reset();
+        mFBO.reset();
+
+        mGLCallbacks->mCleanupCb();
+    }
+}
+
+void HsQMLCanvasBackEnd::doCleanupKill()
+{
+    doCleanup();
+    delete this;
+}
+
 HsQMLCanvas::HsQMLCanvas(QQuickItem* parent)
     : QQuickItem(parent)
     , mWindow(NULL)
     , mBackEnd(NULL)
+    , mStatus(Okay)
+    , mFrontStatus(Okay)
+    , mBackStatus(Okay)
     , mDisplayMode(Inline)
     , mCanvasWidth(0)
     , mCanvasWidthSet(false)
     , mCanvasHeight(0)
     , mCanvasHeightSet(false)
+    , mLoadModel(false)
+    , mValidModel(false)
 {
     QObject::connect(
         this, SIGNAL(windowChanged(QQuickWindow*)),
         this, SLOT(doWindowChanged(QQuickWindow*)));
+    setFlag(ItemHasContents, true);
 }
 
 HsQMLCanvas::~HsQMLCanvas()
@@ -39,14 +335,86 @@
     if (!mCanvasHeightSet) {
         setCanvasHeight(rect.height(), false);
     }
+    update();
 }
 
+QSGNode* HsQMLCanvas::updatePaintNode(
+    QSGNode* oldNode, UpdatePaintNodeData* paintData)
+{
+    // Window always needs repainting
+    mWindow->update();
+
+    // Lazily create new callbacks
+    if (!mGLCallbacks) {
+        mGLCallbacks = mDelegate.value<HsQMLGLDelegate>().makeCallbacks();
+        mLoadModel = true;
+        mValidModel = false;
+
+        // Display nothing without a valid delegate
+        if (!mGLCallbacks) {
+            setStatus(BadDelegate);
+            delete oldNode;
+            return NULL;
+        }
+    }
+
+    // Process model update
+    if (mLoadModel) {
+        mValidModel = mGLCallbacks->mSyncCb(
+            reinterpret_cast<HsQMLJValHandle*>(&mModel));
+        mLoadModel = false;
+    }
+
+    // Display nothing if there's no valid model
+    if (!mValidModel) {
+        setStatus(BadModel);
+        detachBackEnd();
+        delete oldNode;
+        return NULL;
+    }
+
+    // Create back-end on the rendering thread
+    if (!mBackEnd) {
+        mBackEnd = new HsQMLCanvasBackEnd(mWindow, mGLCallbacks, mDisplayMode);
+
+        // Monitor back-end's status
+        QObject::connect(
+            mBackEnd, SIGNAL(statusChanged(HsQMLCanvas::Status)),
+            this, SLOT(doBackEndStatusChanged(HsQMLCanvas::Status)));
+        setStatus(mBackEnd->status(), true);
+    }
+    setStatus(Okay);
+
+    // Save pointer to transform node
+    mBackEnd->setTransformNode(
+        Inline != mDisplayMode ? paintData->transformNode : NULL,
+        width(), height());
+
+    // Produce texture node if needed
+    if (QSGTexture* texture =
+            mBackEnd->updateFBO(mCanvasWidth, mCanvasHeight)) {
+        QSGSimpleTextureNode* n = static_cast<QSGSimpleTextureNode*>(oldNode);
+        if (!n) {
+            n = new QSGSimpleTextureNode();
+        }
+        n->setRect(0, 0, width(), height());
+        n->setTexture(texture);
+        return n;
+    }
+
+    delete oldNode;
+    return NULL;
+}
+
 void HsQMLCanvas::detachBackEnd()
 {
     if (mBackEnd) {
         // The back-end belongs to the rendering thread
-        mBackEnd->deleteLater();
+        QMetaObject::invokeMethod(
+            mBackEnd, "doCleanupKill", Qt::QueuedConnection);
+        QObject::disconnect(mBackEnd, 0, this, 0);
         mBackEnd = NULL;
+        mGLCallbacks.reset();
     }
 }
 
@@ -60,7 +428,9 @@
     bool change = mDisplayMode != mode;
     mDisplayMode = mode;
     if (change) {
+        detachBackEnd();
         displayModeChanged();
+        update();
     }
 }
 
@@ -76,6 +446,7 @@
     mCanvasWidthSet |= set;
     if (change) {
         canvasWidthChanged();
+        update();
     }
 }
 
@@ -97,6 +468,7 @@
     mCanvasHeightSet |= set;
     if (change) {
         canvasHeightChanged();
+        update();
     }
 }
 
@@ -106,37 +478,104 @@
     setCanvasHeight(height(), false);
 }
 
-void HsQMLCanvas::doWindowChanged(QQuickWindow* win)
+QVariant HsQMLCanvas::delegate() const
 {
-    if (mWindow) {
-        QObject::disconnect(mWindow, NULL, this, NULL);
+    return mDelegate;
+}
+
+void HsQMLCanvas::setDelegate(const QVariant& d)
+{
+    bool change = mDelegate != d;
+    mDelegate = d;
+    if (change) {
         detachBackEnd();
+        delegateChanged();
+        update();
     }
-    mWindow = win;
-    if (mWindow) {
-        QObject::connect(
-            mWindow, SIGNAL(beforeSynchronizing()),
-            this, SLOT(doPreSynchronise()), Qt::DirectConnection);
+}
+
+QJSValue HsQMLCanvas::model() const
+{
+    return mModel;
+}
+
+void HsQMLCanvas::setModel(const QJSValue& m)
+{
+    bool change = !mModel.strictlyEquals(m);
+    mModel = m;
+    if (change) {
+        modelChanged();
+        mLoadModel = true;
+        update();
     }
 }
 
-void HsQMLCanvas::doPreSynchronise()
+HsQMLCanvas::Status HsQMLCanvas::status() const
 {
-    // This executes on the rendering thread
-    if (!mBackEnd) {
-        mBackEnd = new HsQMLCanvasBackEnd(mWindow);
+    return mStatus;
+}
+
+void HsQMLCanvas::setStatus(Status status, bool backEnd)
+{
+    if (backEnd) {
+        mBackStatus = status;
     }
-    mBackEnd->setModeSize(mDisplayMode, mCanvasWidth, mCanvasHeight);
+    else {
+        mFrontStatus = status;
+    }
+    Status newStatus = mFrontStatus == Okay ? mBackStatus : mFrontStatus;
+    bool change = mStatus != newStatus;
+    if (change) {
+        statusChanged();
+    }
 }
 
-HsQMLGLControl::HsQMLGLControl(QQuickItem* parent)
-    : QQuickItem(parent)
-{}
+void HsQMLCanvas::doWindowChanged(QQuickWindow* win)
+{
+    detachBackEnd();
+    mWindow = win;
+}
 
-void hsqml_init_jval_gldelegate(
-    HsQMLJValHandle* hndl,
-    HsQMLGLPaintCb paintCb)
+void HsQMLCanvas::doBackEndStatusChanged(Status status)
 {
-    QJSValue* value = reinterpret_cast<QJSValue*>(hndl);
-    HsQMLGLDelegate delegate(paintCb);
+    setStatus(status, true);
+}
+
+HsQMLGLDelegateHandle* hsqml_create_gldelegate()
+{
+    return reinterpret_cast<HsQMLGLDelegateHandle*>(new HsQMLGLDelegate());
+}
+
+void hsqml_finalise_gldelegate_handle(
+    HsQMLGLDelegateHandle* hndl)
+{
+    HsQMLGLDelegate* delegate = reinterpret_cast<HsQMLGLDelegate*>(hndl);
+    delete delegate;
+}
+
+void hsqml_gldelegate_setup(
+    HsQMLGLDelegateHandle* hndl,
+    HsQMLGLMakeCallbacksCb makeContextCb)
+{
+    HsQMLGLDelegate* delegate = reinterpret_cast<HsQMLGLDelegate*>(hndl);
+    delegate->setup(makeContextCb);
+}
+
+void hsqml_gldelegate_to_jval(
+    HsQMLGLDelegateHandle* hndl,
+    HsQMLJValHandle* jhndl)
+{
+    HsQMLGLDelegate* delegate = reinterpret_cast<HsQMLGLDelegate*>(hndl);
+    new((void*)jhndl) QJSValue(
+        gManager->activeEngine()->declEngine()->toScriptValue(*delegate));
+}
+
+void hsqml_gldelegate_from_jval(
+    HsQMLGLDelegateHandle* hndl,
+    HsQMLJValHandle* jhndl)
+{
+    HsQMLGLDelegate* delegate = reinterpret_cast<HsQMLGLDelegate*>(hndl);
+    QJSValue* value = reinterpret_cast<QJSValue*>(jhndl);
+    *delegate = gManager->activeEngine()->declEngine()->
+        fromScriptValue<HsQMLGLDelegate>(*value); 
 }
diff --git a/cbits/Canvas.h b/cbits/Canvas.h
--- a/cbits/Canvas.h
+++ b/cbits/Canvas.h
@@ -1,27 +1,93 @@
 #ifndef HSQML_CANVAS_H
 #define HSQML_CANVAS_H
 
+#include <QtCore/QObject>
 #include <QtCore/QExplicitlySharedDataPointer>
+#include <QtCore/QMetaType>
+#include <QtCore/QScopedPointer>
+#include <QtCore/QSharedData>
+#include <QtGui/qopengl.h>
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QOpenGLFramebufferObject>
 #include <QtQuick/QQuickItem>
 
 #include "hsqml.h"
 
+class QSGTexture;
+class QSGTransformNode;
+class QQuickWindow;
 class HsQMLCanvasBackEnd;
-class HsQMLGLDelegateImpl;
+class HsQMLWindowInfoImpl;
 
+class HsQMLGLCallbacks : public QSharedData
+{
+public:
+    HsQMLGLCallbacks(
+        HsQMLGLSetupCb, HsQMLGLCleanupCb, HsQMLGLSyncCb, HsQMLGLPaintCb);
+    ~HsQMLGLCallbacks();
+
+    HsQMLGLSetupCb mSetupCb;
+    HsQMLGLCleanupCb mCleanupCb;
+    HsQMLGLSyncCb mSyncCb;
+    HsQMLGLPaintCb mPaintCb;
+};
+
+class HsQMLGLDelegateImpl : public QSharedData
+{
+public:
+    HsQMLGLDelegateImpl(HsQMLGLMakeCallbacksCb);
+    ~HsQMLGLDelegateImpl();
+
+    HsQMLGLMakeCallbacksCb mMakeCallbacksCb;
+};
+
 class HsQMLGLDelegate
 {
 public:
-    HsQMLGLDelegate(HsQMLGLPaintCb);
+    HsQMLGLDelegate();
     ~HsQMLGLDelegate();
+    void setup(HsQMLGLMakeCallbacksCb);
+    typedef QExplicitlySharedDataPointer<HsQMLGLCallbacks> CallbacksRef;
+    CallbacksRef makeCallbacks();
 
 private:
     QExplicitlySharedDataPointer<HsQMLGLDelegateImpl> mImpl;
 };
 
+Q_DECLARE_METATYPE(HsQMLGLDelegate)
+
+class HsQMLWindowInfoImpl : public QSharedData
+{
+public:
+    HsQMLWindowInfoImpl(QQuickWindow*);
+    
+private:
+    friend class HsQMLWindowInfo;
+    QQuickWindow* mWin;
+    int mBelowCount;
+    bool mBelowClear;
+};
+
+class HsQMLWindowInfo
+{
+public:
+    HsQMLWindowInfo();
+    static HsQMLWindowInfo getWindowInfo(QQuickWindow*);
+    void addBelow();
+    void removeBelow();
+    bool needsBelowClear();
+    void endFrame();
+
+private:
+    QExplicitlySharedDataPointer<HsQMLWindowInfoImpl> mImpl;
+};
+
+Q_DECLARE_METATYPE(HsQMLWindowInfo)
+
 class HsQMLCanvas : public QQuickItem
 {
     Q_OBJECT
+    Q_ENUMS(Status)
     Q_ENUMS(DisplayMode)
     Q_PROPERTY(DisplayMode displayMode READ displayMode WRITE setDisplayMode
         NOTIFY displayModeChanged)
@@ -29,8 +95,14 @@
         RESET unsetCanvasWidth NOTIFY canvasWidthChanged)
     Q_PROPERTY(qreal canvasHeight READ canvasHeight WRITE setCanvasHeight
         RESET unsetCanvasHeight NOTIFY canvasHeightChanged)
+    Q_PROPERTY(QVariant delegate READ delegate WRITE setDelegate
+        NOTIFY delegateChanged)
+    Q_PROPERTY(QJSValue model READ model WRITE setModel
+        NOTIFY modelChanged)
+    Q_PROPERTY(Status status READ status NOTIFY statusChanged)
 
 public:
+    enum Status {Okay, BadDelegate, BadModel, BadConfig, BadProcs, BadBind};
     enum DisplayMode {Above, Below, Inline};
 
     HsQMLCanvas(QQuickItem* = NULL);
@@ -40,6 +112,7 @@
     Q_DISABLE_COPY(HsQMLCanvas);
 
     void geometryChanged(const QRectF&, const QRectF&) Q_DECL_OVERRIDE;
+    QSGNode* updatePaintNode(QSGNode*, UpdatePaintNodeData*) Q_DECL_OVERRIDE;
     void detachBackEnd();
     DisplayMode displayMode() const;
     void setDisplayMode(DisplayMode);
@@ -49,30 +122,82 @@
     qreal canvasHeight() const;
     void setCanvasHeight(qreal, bool = true);
     void unsetCanvasHeight();
+    QVariant delegate() const;
+    void setDelegate(const QVariant&);
+    QJSValue model() const;
+    void setModel(const QJSValue&);
+    Status status() const;
+    void setStatus(Status, bool = false);
     Q_SIGNAL void displayModeChanged();
     Q_SIGNAL void canvasWidthChanged();
     Q_SIGNAL void canvasHeightChanged();
+    Q_SIGNAL void delegateChanged();
+    Q_SIGNAL void modelChanged();
+    Q_SIGNAL void statusChanged();
     Q_SLOT void doWindowChanged(QQuickWindow*);
-    Q_SLOT void doPreSynchronise();
+    Q_SLOT void doBackEndStatusChanged(HsQMLCanvas::Status);
 
     QQuickWindow* mWindow;
     HsQMLCanvasBackEnd* mBackEnd;
+    Status mStatus;
+    Status mFrontStatus;
+    Status mBackStatus;
     DisplayMode mDisplayMode;
     qreal mCanvasWidth;
     bool mCanvasWidthSet;
     qreal mCanvasHeight;
     bool mCanvasHeightSet;
+    QVariant mDelegate;
+    HsQMLGLDelegate::CallbacksRef mGLCallbacks;
+    QJSValue mModel;
+    bool mLoadModel;
+    bool mValidModel;
 };
 
-class HsQMLGLControl : public QQuickItem
+class HsQMLCanvasBackEnd : public QObject
 {
     Q_OBJECT
 
 public:
-    HsQMLGLControl(QQuickItem* = NULL);
+    HsQMLCanvasBackEnd(
+        QQuickWindow*,
+        const HsQMLGLDelegate::CallbacksRef&,
+        HsQMLCanvas::DisplayMode);
+    ~HsQMLCanvasBackEnd();
+    void setTransformNode(QSGTransformNode*, qreal, qreal);
+    QSGTexture* updateFBO(qreal, qreal);
+    HsQMLCanvas::Status status() const;
 
 private:
-    Q_DISABLE_COPY(HsQMLGLControl);
+    Q_DISABLE_COPY(HsQMLCanvasBackEnd)
+
+    void setStatus(HsQMLCanvas::Status);
+    Q_SLOT void doRendering();
+    Q_SLOT void doEndFrame();
+    Q_SLOT void doCleanup();
+    Q_SLOT void doCleanupKill();
+    Q_SIGNAL void statusChanged(HsQMLCanvas::Status);
+
+    typedef void (*GLViewportFn)(GLint,GLint,GLsizei,GLsizei);
+    typedef void (*GLClearColorFn)(GLclampf,GLclampf,GLclampf,GLclampf);
+    typedef void (*GLClearFn)(GLbitfield);
+
+    QQuickWindow* mWindow;
+    HsQMLWindowInfo mWinInfo;
+    HsQMLGLDelegate::CallbacksRef mGLCallbacks;
+    QOpenGLContext* mGL;
+    GLViewportFn mGLViewportFn;
+    GLClearColorFn mGLClearColorFn;
+    GLClearFn mGLClearFn;
+    HsQMLCanvas::Status mStatus;
+    HsQMLCanvas::DisplayMode mDisplayMode;
+    qreal mItemWidth;
+    qreal mItemHeight;
+    QSGTransformNode* mTransformNode;
+    qreal mCanvasWidth;
+    qreal mCanvasHeight;
+    QScopedPointer<QOpenGLFramebufferObject> mFBO;
+    QScopedPointer<QSGTexture> mTexture;
 };
 
 #endif //HSQML_CANVAS_H
diff --git a/cbits/CanvasImpl.cpp b/cbits/CanvasImpl.cpp
deleted file mode 100644
--- a/cbits/CanvasImpl.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-#include <QtCore/qmath.h>
-
-#include "CanvasImpl.h"
-#include "Manager.h"
-
-HsQMLGLDelegateImpl::HsQMLGLDelegateImpl(HsQMLGLPaintCb paintCb)
-    : mPaintCb(paintCb)
-{}
-
-HsQMLGLDelegateImpl::~HsQMLGLDelegateImpl()
-{
-    gManager->freeFun(reinterpret_cast<HsFunPtr>(mPaintCb));
-}
-
-HsQMLCanvasBackEnd::HsQMLCanvasBackEnd(QQuickWindow* win)
-{
-    QObject::connect(
-        win, SIGNAL(beforeRendering()),
-        this, SLOT(doRendering()));
-}
-
-HsQMLCanvasBackEnd::~HsQMLCanvasBackEnd()
-{
-}
-
-void HsQMLCanvasBackEnd::setModeSize(
-    HsQMLCanvas::DisplayMode mode, qreal w, qreal h)
-{
-    mDisplayMode = mode;
-    mCanvasWidth = w;
-    mCanvasHeight = h;
-
-    if (HsQMLCanvas::Inline == mode) {
-        if (!mFBO || mFBO->width() != w || mFBO->height() != h) {
-            mFBO.reset(new QOpenGLFramebufferObject(
-                qCeil(w), qCeil(h), QOpenGLFramebufferObject::Depth));
-        }
-    }
-    else {
-        mFBO.reset();
-    }
-}
-
-void HsQMLCanvasBackEnd::doRendering()
-{
-    bool inlineMode = HsQMLCanvas::Inline == mDisplayMode;
-    if (inlineMode && !mFBO) {
-        return;
-    }
-    if (inlineMode) {
-        mFBO->bind();
-    }
-    if (inlineMode) {
-        mFBO->release();
-    }
-}
diff --git a/cbits/CanvasImpl.h b/cbits/CanvasImpl.h
deleted file mode 100644
--- a/cbits/CanvasImpl.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#ifndef HSQML_CANVAS_IMPL_H
-#define HSQML_CANVAS_IMPL_H
-
-#include <QtCore/QObject>
-#include <QtCore/QScopedPointer>
-#include <QtCore/QSharedData>
-#include <QtGui/QOpenGLFramebufferObject>
-#include <QtQuick/QQuickWindow>
-
-#include "Canvas.h"
-
-class HsQMLGLDelegateImpl : public QSharedData
-{
-public:
-    HsQMLGLDelegateImpl(HsQMLGLPaintCb);
-    ~HsQMLGLDelegateImpl();
-
-    HsQMLGLPaintCb mPaintCb;
-};
-
-class HsQMLCanvasBackEnd : public QObject
-{
-    Q_OBJECT
-
-public:
-    HsQMLCanvasBackEnd(QQuickWindow*);
-    ~HsQMLCanvasBackEnd();
-    void setModeSize(HsQMLCanvas::DisplayMode, qreal, qreal);
-
-private:
-    Q_DISABLE_COPY(HsQMLCanvasBackEnd)
-
-    Q_SLOT void doRendering();
-
-    HsQMLCanvas::DisplayMode mDisplayMode;
-    qreal mCanvasWidth;
-    qreal mCanvasHeight;
-    QScopedPointer<QOpenGLFramebufferObject> mFBO;
-};
-
-#endif //HSQML_CANVAS_IMPL_H
diff --git a/cbits/Manager.cpp b/cbits/Manager.cpp
--- a/cbits/Manager.cpp
+++ b/cbits/Manager.cpp
@@ -8,6 +8,7 @@
 #include <pthread.h>
 #endif
 
+#include "Canvas.h"
 #include "Manager.h"
 #include "Object.h"
 
@@ -211,8 +212,9 @@
         // Install hooked handler for QVariants
         QVariantPrivate::registerHandler(0, &mHookedHandler);
 
-        // Register custom type
+        // Register custom types
         qRegisterMetaType<HsQMLEngineConfig>("HsQMLEngineConfig");
+        qmlRegisterType<HsQMLCanvas>("HsQML.Canvas", 1, 0, "HaskellCanvas");
 
         // Create application object
         mApp = new HsQMLManagerApp();
diff --git a/cbits/Object.cpp b/cbits/Object.cpp
--- a/cbits/Object.cpp
+++ b/cbits/Object.cpp
@@ -1,13 +1,28 @@
 #include <HsFFI.h>
 #include <QtCore/QString>
+#include <QtCore/QMutexLocker>
 #include <QtQml/QQmlEngine>
 
 #include "Object.h"
 #include "Class.h"
 #include "Manager.h"
 
-static const char* cRefSrcNames[] = {"Hndl", "Obj", "Event", "Var"};
+static const char* cRefSrcNames[] = {"Hndl", "Weak", "Obj", "Event", "Var"};
 
+HsQMLObjectFinaliser::HsQMLObjectFinaliser(HsQMLObjFinaliserCb cb)
+    : mFinaliseCb(cb)
+{}
+
+HsQMLObjectFinaliser::~HsQMLObjectFinaliser()
+{
+    gManager->freeFun(reinterpret_cast<HsFunPtr>(mFinaliseCb));
+}
+
+void HsQMLObjectFinaliser::finalise(HsQMLObjectProxy* proxy) const
+{
+    mFinaliseCb(reinterpret_cast<HsQMLObjectHandle*>(proxy));
+}
+
 HsQMLObjectProxy::HsQMLObjectProxy(HsStablePtr haskell, HsQMLClass* klass)
     : mHaskell(haskell)
     , mKlass(klass)
@@ -60,6 +75,7 @@
         mKlass->name(), mSerial, mObject));
 
     mObject = NULL;
+    runFinalisers();
 }
 
 void HsQMLObjectProxy::tryGCLock()
@@ -79,15 +95,43 @@
 {
     Q_ASSERT(gManager->isEventThread());
 
-    if (mObject && mHndlCount.loadAcquire() == 0 && mObject->isGCLocked()) {
-        mObject->clearGCLock();
+    if (mObject && mHndlCount.loadAcquire() == 0) {
+        if (mObject->isGCLocked()) {
+            mObject->clearGCLock();
 
-        HSQML_LOG(5,
-            QString().sprintf("Unlock QObject, class=%s, id=%d, qptr=%p.",
-            mKlass->name(), mSerial, mObject));
+            HSQML_LOG(5,
+                QString().sprintf("Unlock QObject, class=%s, id=%d, qptr=%p.",
+                mKlass->name(), mSerial, mObject));
+        }
+        else {
+            // If there had been a QML object then this would have happened
+            // when the QML GC collected it.
+            runFinalisers();
+        }
     }
 }
 
+void HsQMLObjectProxy::addFinaliser(const HsQMLObjectFinaliser::Ref& f)
+{
+    QMutexLocker locker(&mFinaliseMutex);
+    mFinalisers.append(f);
+}
+
+void HsQMLObjectProxy::runFinalisers()
+{
+    // Copy and clear finalisers under lock
+    mFinaliseMutex.lock();
+    const Finalisers fs = mFinalisers;
+    mFinalisers.clear();
+    mFinaliseMutex.unlock();
+
+    // Call finalisers outside lock so they can re-addFinaliser()
+    Q_FOREACH(const HsQMLObjectFinaliser::Ref& f, fs) {
+        ref(Handle);
+        f->finalise(this);
+    }
+}
+
 HsQMLEngine* HsQMLObjectProxy::engine() const
 {
     if (mObject != NULL) {
@@ -112,7 +156,7 @@
 
 void HsQMLObjectProxy::deref(RefSrc src)
 {
-    // Remove JavaScript GC lock when there are no handles
+    // Remove JavaScript GC lock when there are no strong handles
     if (src == Handle || src == Variant) {
         int hndlCount = mHndlCount.fetchAndAddOrdered(-1);
         if (hndlCount == 1 && mObject) {
@@ -340,6 +384,14 @@
     return hsqml_get_object_from_pointer(jval->toQObject());
 }
 
+extern void hsqml_object_reference_handle(
+    HsQMLObjectHandle* hndl,
+    int weak)
+{
+    HsQMLObjectProxy* proxy = (HsQMLObjectProxy*)hndl;
+    proxy->ref(weak ? HsQMLObjectProxy::WeakHandle : HsQMLObjectProxy::Handle);
+}
+
 extern void hsqml_finalise_object_handle(
     HsQMLObjectHandle* hndl)
 {
@@ -349,6 +401,15 @@
     }
 }
 
+extern void hsqml_finalise_object_weak_handle(
+    HsQMLObjectHandle* hndl)
+{
+    if (hndl) {
+        HsQMLObjectProxy* proxy = (HsQMLObjectProxy*)hndl;
+        proxy->deref(HsQMLObjectProxy::WeakHandle);
+    }
+}
+
 extern void hsqml_fire_signal(
     HsQMLObjectHandle* hndl, int idx, void** args)
 {
@@ -362,4 +423,26 @@
         HsQMLObject* obj = proxy->object(engine);
         QMetaObject::activate(obj, proxy->klass()->metaObj(), idx, args);
     }
+}
+
+extern HsQMLObjFinaliserHandle* hsqml_create_obj_finaliser(
+    HsQMLObjFinaliserCb cb)
+{
+    return reinterpret_cast<HsQMLObjFinaliserHandle*>(
+        new HsQMLObjectFinaliser::Ref(new HsQMLObjectFinaliser(cb)));
+}
+
+extern void hsqml_finalise_obj_finaliser(
+    HsQMLObjFinaliserHandle* hndl)
+{
+    HsQMLObjectFinaliser::Ref* fp =
+        reinterpret_cast<HsQMLObjectFinaliser::Ref*>(hndl);
+    delete fp;
+}
+
+extern void hsqml_object_add_finaliser(
+    HsQMLObjectHandle* hndl, HsQMLObjFinaliserHandle* fhndl)
+{
+    HsQMLObjectProxy* proxy = reinterpret_cast<HsQMLObjectProxy*>(hndl);
+    proxy->addFinaliser(*reinterpret_cast<HsQMLObjectFinaliser::Ref*>(fhndl));
 }
diff --git a/cbits/Object.h b/cbits/Object.h
--- a/cbits/Object.h
+++ b/cbits/Object.h
@@ -4,12 +4,33 @@
 #include <QtCore/QObject>
 #include <QtCore/QAtomicInt>
 #include <QtCore/QEvent>
+#include <QtCore/QExplicitlySharedDataPointer>
+#include <QtCore/QMutex>
+#include <QtCore/QVarLengthArray>
 #include <QtQml/QJSValue>
 
+#include "hsqml.h"
+
 class HsQMLEngine;
 class HsQMLClass;
 class HsQMLObject;
+class HsQMLObjectProxy;
 
+class HsQMLObjectFinaliser : public QSharedData
+{
+public:
+    typedef QExplicitlySharedDataPointer<HsQMLObjectFinaliser> Ref;
+
+    HsQMLObjectFinaliser(HsQMLObjFinaliserCb);
+    ~HsQMLObjectFinaliser();
+    void finalise(HsQMLObjectProxy*) const;
+
+private:
+    Q_DISABLE_COPY(HsQMLObjectFinaliser);
+
+    HsQMLObjFinaliserCb mFinaliseCb;
+};
+
 class HsQMLObjectProxy
 {
 public:
@@ -21,18 +42,25 @@
     void clearObject();
     void tryGCLock();
     void removeGCLock();
+    void addFinaliser(const HsQMLObjectFinaliser::Ref&);
+    void runFinalisers();
     HsQMLEngine* engine() const;
-    enum RefSrc {Handle, Object, Event, Variant};
+    enum RefSrc {Handle, WeakHandle, Object, Event, Variant};
     void ref(RefSrc);
     void deref(RefSrc);
 
 private:
+    Q_DISABLE_COPY(HsQMLObjectProxy);
+
     HsStablePtr mHaskell;
     HsQMLClass* mKlass;
     int mSerial;
     HsQMLObject* volatile mObject;
     QAtomicInt mRefCount;
     QAtomicInt mHndlCount;
+    QMutex mFinaliseMutex;
+    typedef QVarLengthArray<HsQMLObjectFinaliser::Ref, 1> Finalisers;
+    Finalisers mFinalisers;
 };
 
 class HsQMLObjectEvent : public QEvent
@@ -62,6 +90,8 @@
     HsQMLEngine* engine() const;
 
 private:
+    Q_DISABLE_COPY(HsQMLObject);
+
     HsQMLObjectProxy* mProxy;
     HsStablePtr mHaskell;
     HsQMLClass* mKlass;
diff --git a/cbits/hsqml.h b/cbits/hsqml.h
--- a/cbits/hsqml.h
+++ b/cbits/hsqml.h
@@ -162,17 +162,79 @@
 extern HsQMLObjectHandle* hsqml_get_object_from_jval(
     HsQMLJValHandle*);
 
+extern void hsqml_object_reference_handle(
+    HsQMLObjectHandle*, int);
+
 extern void hsqml_finalise_object_handle(
     HsQMLObjectHandle*);
 
+extern void hsqml_finalise_object_weak_handle(
+    HsQMLObjectHandle*);
+
 extern void hsqml_fire_signal(
     HsQMLObjectHandle*, int, void**);
 
+/* Object Finaliser */
+typedef char HsQMLObjFinaliserHandle;
+
+typedef void (*HsQMLObjFinaliserCb)(HsQMLObjectHandle*);
+
+extern HsQMLObjFinaliserHandle* hsqml_create_obj_finaliser(
+    HsQMLObjFinaliserCb);
+
+extern void hsqml_finalise_obj_finaliser(
+    HsQMLObjFinaliserHandle*);
+
+extern void hsqml_object_add_finaliser(
+    HsQMLObjectHandle*, HsQMLObjFinaliserHandle*);
+
 /* Engine */
 extern void hsqml_create_engine(
     HsQMLObjectHandle*,
     HsQMLStringHandle*,
     HsQMLTrivialCb stopCb);
+
+/* Canvas */
+typedef char HsQMLGLDelegateHandle;
+
+typedef enum {
+    HSQML_GL_DESKTOP,
+    HSQML_GL_ES
+} HsQMLGLCanvasType;
+
+typedef void (*HsQMLGLSetupCb)(
+    HsQMLGLCanvasType, int, int);
+
+typedef void (*HsQMLGLCleanupCb)();
+
+typedef int (*HsQMLGLSyncCb)(
+    HsQMLJValHandle*);
+
+typedef void (*HsQMLGLPaintCb)(
+    float*, float, float);
+
+typedef void (*HsQMLGLMakeCallbacksCb)(
+    HsQMLGLSetupCb*,
+    HsQMLGLCleanupCb*,
+    HsQMLGLSyncCb*,
+    HsQMLGLPaintCb*);
+
+extern HsQMLGLDelegateHandle* hsqml_create_gldelegate();
+
+extern void hsqml_finalise_gldelegate_handle(
+    HsQMLGLDelegateHandle*);
+
+extern void hsqml_gldelegate_setup(
+    HsQMLGLDelegateHandle*,
+    HsQMLGLMakeCallbacksCb);
+
+extern void hsqml_gldelegate_to_jval(
+    HsQMLGLDelegateHandle*,
+    HsQMLJValHandle*);
+
+extern void hsqml_gldelegate_from_jval(
+    HsQMLGLDelegateHandle*,
+    HsQMLJValHandle*);
 
 #ifdef __cplusplus
 }
diff --git a/hsqml.cabal b/hsqml.cabal
--- a/hsqml.cabal
+++ b/hsqml.cabal
@@ -1,5 +1,5 @@
 Name:               hsqml
-Version:            0.3.1.1
+Version:            0.3.2.0
 Cabal-version:      >= 1.14
 Build-type:         Custom
 License:            BSD3
@@ -41,25 +41,30 @@
         base         == 4.*,
         containers   >= 0.4 && < 0.6,
         filepath     == 1.3.*,
-        text         >= 0.11 && < 1.2,
+        text         >= 0.11 && < 1.3,
         tagged       >= 0.4 && < 0.8,
         transformers >= 0.2 && < 0.5
     Exposed-modules:
         Graphics.QML
         Graphics.QML.Debug
+        Graphics.QML.Canvas
         Graphics.QML.Engine
         Graphics.QML.Marshal
         Graphics.QML.Objects
+        Graphics.QML.Objects.Weak
     Other-modules:
         Graphics.QML.Internal.BindPrim
+        Graphics.QML.Internal.BindCanvas
         Graphics.QML.Internal.BindObj
         Graphics.QML.Internal.BindCore
         Graphics.QML.Internal.JobQueue
         Graphics.QML.Internal.Marshal
         Graphics.QML.Internal.MetaObj
+        Graphics.QML.Internal.Objects
         Graphics.QML.Internal.Types
     Hs-source-dirs: src
     C-sources:
+        cbits/Canvas.cpp
         cbits/Class.cpp
         cbits/Engine.cpp
         cbits/Intrinsics.cpp
@@ -67,6 +72,7 @@
         cbits/Object.cpp
     Include-dirs: cbits
     X-moc-headers:
+        cbits/Canvas.h
         cbits/Engine.h
         cbits/Manager.h
     X-separate-cbits: True
@@ -105,7 +111,7 @@
         base       == 4.*,
         containers >= 0.4 && < 0.6,
         directory  >= 1.1 && < 1.3,
-        text       >= 0.11 && < 1.2,
+        text       >= 0.11 && < 1.3,
         tagged     >= 0.4 && < 0.8,
         QuickCheck >= 2.4 && < 2.8,
         hsqml      == 0.3.*
diff --git a/src/Graphics/QML.hs b/src/Graphics/QML.hs
--- a/src/Graphics/QML.hs
+++ b/src/Graphics/QML.hs
@@ -19,9 +19,11 @@
 -- * Graphics.QML
   module Graphics.QML.Engine,
   module Graphics.QML.Marshal,
-  module Graphics.QML.Objects
+  module Graphics.QML.Objects,
+  module Graphics.QML.Objects.Weak
 ) where
 
 import Graphics.QML.Engine
 import Graphics.QML.Marshal
 import Graphics.QML.Objects
+import Graphics.QML.Objects.Weak
diff --git a/src/Graphics/QML/Canvas.hs b/src/Graphics/QML/Canvas.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/QML/Canvas.hs
@@ -0,0 +1,157 @@
+{-# LANGUAGE
+    ScopedTypeVariables,
+    TypeFamilies,
+    FlexibleInstances
+  #-}
+
+{-| Facility for drawing OpenGL graphics into the QML scenegraph.
+
+To use this facility, you must place a @HaskellCanvas@ item into your
+QML scene. This item can be imported from the @HsQML.Canvas 1.0@ namespace
+using the @import@ statement in your QML script. It has several properties
+which can be set from QML:
+
+[@displayMode@] Specifies how the canvas is rendered with respect to the
+rest of the scene. Possible values are:
+
+    [@HaskellCanvas.Above@] The canvas shares a buffer with the scenegraph
+    and is painted top of other items.
+    [@HaskellCanvas.Inline@] The canvas has its own framebuffer object and the
+    contents of this buffer are painted inline with other items (default).
+    [@HaskellCanvas.Below@] The canvas shares a buffer with the scenegraph
+    and is painted underneath other items.
+
+[@canvasWidth@] Width of the framebuffer object in pixels. Defaults to the
+item width.
+[@canvasHeight@] Height of the framebuffer object in pixels. Defaults to the
+item height.
+[@delegate@] A marshalled 'OpenGLDelegate' value which specifies the Haskell
+functions used to render the canvas.
+[@model@] A value passed to delegate's paint function. The canvas is
+repainted whenever this value changes.
+[@status@] Either @HaskellCanvas.Okay@ or an error code (read only).
+-}
+module Graphics.QML.Canvas (
+    OpenGLDelegate,
+    newOpenGLDelegate,
+    OpenGLType (
+        OpenGLDesktop,
+        OpenGLES),
+    OpenGLSetup,
+    openGLType,
+    openGLMajor,
+    openGLMinor,
+    OpenGLPaint,
+    OpenGLPaint',
+    setupData,
+    modelData,
+    matrixPtr,
+    itemWidth,
+    itemHeight
+) where
+
+import Graphics.QML.Internal.BindCanvas
+import Graphics.QML.Internal.BindPrim
+import Graphics.QML.Internal.Marshal
+import Graphics.QML.Marshal
+
+import Data.IORef
+import Data.Maybe
+import Data.Tagged
+import Control.Monad.Trans.Maybe
+import Foreign.Ptr (Ptr)
+import Foreign.C.Types (CFloat)
+
+-- | Delegate for painting OpenGL graphics.
+newtype OpenGLDelegate = OpenGLDelegate HsQMLGLDelegateHandle
+
+instance Marshal OpenGLDelegate where
+    type MarshalMode OpenGLDelegate c d = ModeBidi c
+    marshaller = Marshaller {
+        mTypeCVal_ = Tagged tyJSValue,
+        mFromCVal_ = jvalFromCVal,
+        mToCVal_ = jvalToCVal,
+        mWithCVal_ = jvalWithCVal,
+        mFromJVal_ = \ptr -> MaybeT $ do
+            hndl <- hsqmlCreateGldelegate
+            hsqmlGldelegateFromJval hndl ptr
+            return $ Just $ OpenGLDelegate hndl,
+        mWithJVal_ = \(OpenGLDelegate hndl) f ->
+            withJVal (flip hsqmlGldelegateToJval) hndl f,
+        mFromHndl_ = unimplFromHndl,
+        mToHndl_ = unimplToHndl}
+
+-- | Represents the type of an OpenGL context.
+data OpenGLType
+    -- | Desktop OpenGL context.
+    = OpenGLDesktop
+    -- | OpenGL ES context.
+    | OpenGLES
+    deriving (Eq, Show)
+
+mapGLType :: HsQMLGLCanvasType -> OpenGLType
+mapGLType HsqmlGlDesktop = OpenGLDesktop
+mapGLType HsqmlGlEs      = OpenGLES
+
+-- | Encapsulates parameters for OpenGL setup.
+data OpenGLSetup = OpenGLSetup {
+    -- | Type of OpenGL context.
+    openGLType :: OpenGLType,
+    -- | Major version number of OpenGL context.
+    openGLMajor :: Int,
+    -- | Minor version number of OpenGL context.
+    openGLMinor :: Int
+}
+
+-- | Encapsulates parameters for OpenGL paint.
+data OpenGLPaint s m = OpenGLPaint {
+    -- | Gets the setup state.
+    setupData  :: s,
+    -- | Gets the active model.
+    modelData :: m,
+    -- | Pointer to a 4 by 4 matrix which transform coordinates in the range
+    -- (-1, -1) to (1, 1) on to the target rectangle in the scene.
+    matrixPtr :: Ptr CFloat,
+    -- | Width of the canvas item in its local coordinate system.
+    itemWidth :: Float,
+    -- | Height of the canvas item in its local coordinate system.
+    itemHeight :: Float
+}
+
+-- | Specialised version of `OpenGLPaint` with no model.
+type OpenGLPaint' s = OpenGLPaint s Ignored
+
+newOpenGLCallbacks :: (Marshal m, CanGetFrom m ~ Yes) =>
+    (OpenGLSetup -> IO i) -> (OpenGLPaint i m -> IO ()) -> (i -> IO ()) ->
+    CallbacksFactory
+newOpenGLCallbacks setupFn paintFn cleanupFn = do
+    iRef <- newIORef Nothing
+    mRef <- newIORef Nothing
+    let setupCb ctype major minor = do
+            iVal <- setupFn $ OpenGLSetup
+                (mapGLType $ cIntToEnum ctype)
+                (fromIntegral major) (fromIntegral minor)
+            writeIORef iRef $ Just iVal
+        cleanupCb = do
+            iVal <- readIORef iRef
+            cleanupFn $ fromJust iVal
+        syncCb ptr = do
+             mVal <- runMaybeT $ mFromJVal ptr
+             writeIORef mRef mVal
+             return $ if isJust mVal then 1 else 0
+        paintCb mPtr w h = do
+            iVal <- readIORef iRef
+            mVal <- readIORef mRef
+            paintFn $ OpenGLPaint
+                (fromJust iVal) (fromJust mVal)
+                mPtr (realToFrac w) (realToFrac h)
+    return (setupCb, cleanupCb, syncCb, paintCb)
+
+-- | Creates a new 'OpenGLDelegate' from setup, paint, and cleanup functions.
+newOpenGLDelegate :: (Marshal m, CanGetFrom m ~ Yes) =>
+    (OpenGLSetup -> IO i) -> (OpenGLPaint i m -> IO ()) -> (i -> IO ()) ->
+    IO OpenGLDelegate
+newOpenGLDelegate setupFn paintFn cleanupFn = do
+    hndl <- hsqmlCreateGldelegate
+    hsqmlGldelegateSetup hndl (newOpenGLCallbacks setupFn paintFn cleanupFn)
+    return $ OpenGLDelegate hndl
diff --git a/src/Graphics/QML/Engine.hs b/src/Graphics/QML/Engine.hs
--- a/src/Graphics/QML/Engine.hs
+++ b/src/Graphics/QML/Engine.hs
@@ -34,18 +34,16 @@
 import Graphics.QML.Internal.Marshal
 import Graphics.QML.Internal.BindPrim
 import Graphics.QML.Internal.BindCore
-import Graphics.QML.Marshal
+import Graphics.QML.Marshal ()
 import Graphics.QML.Objects
 
 import Control.Applicative
 import Control.Concurrent
-import Control.Concurrent.MVar
 import Control.Exception
 import Control.Monad
 import Control.Monad.IO.Class
 import qualified Data.Text as T
 import Data.List
-import Data.Maybe
 import Data.Traversable
 import Data.Typeable
 import Foreign.Ptr
@@ -75,8 +73,8 @@
     hsqmlInit
     let obj = contextObject config
         DocumentPath res = initialDocument config
-    hndl <- sequenceA $ fmap mToHndl $ obj
-    mWithCVal (T.pack res) $ \resPtr -> do
+    hndl <- sequenceA $ fmap mToHndl obj
+    mWithCVal (T.pack res) $ \resPtr ->
         hsqmlCreateEngine hndl (HsQMLStringHandle $ castPtr resPtr) stopCb
     return Engine
 
@@ -101,8 +99,7 @@
 -- | Starts a new QML engine using the supplied configuration and returns
 -- immediately without blocking.
 runEngineAsync :: EngineConfig -> RunQML Engine
-runEngineAsync config = RunQML $ do
-    runEngineImpl config (return ())
+runEngineAsync config = RunQML $ runEngineImpl config (return ())
 
 -- | Conveniance function that both runs the event loop and starts a new QML
 -- engine. It blocks keeping the event loop running until the engine has
@@ -116,7 +113,7 @@
 newtype RunQML a = RunQML (IO a) deriving (Functor, Applicative, Monad)
 
 instance MonadIO RunQML where
-    liftIO io = RunQML io
+    liftIO = RunQML
 
 -- | This function enters the Qt event loop and executes the supplied function
 -- in the 'RunQML' monad on a new unbound thread. The event loop will continue
@@ -138,7 +135,7 @@
             ret <- try runFn
             case ret of
                 Left ex -> putMVar finishVar $ throwIO (ex :: SomeException)
-                Right ret -> putMVar finishVar $ return ret
+                Right ret' -> putMVar finishVar $ return ret'
             hsqmlEvloopRelease
         yieldCb = if rtsSupportsBoundThreads
                   then Nothing
@@ -151,7 +148,7 @@
             finFn
 
 tryRunInBoundThread :: IO a -> IO a
-tryRunInBoundThread action = do
+tryRunInBoundThread action =
     if rtsSupportsBoundThreads
     then runInBoundThread action
     else action
@@ -195,15 +192,15 @@
 fileDocument :: FilePath -> DocumentPath
 fileDocument fp =
     let ds = splitDirectories fp
-        abs = isAbsolute fp
+        isAbs = isAbsolute fp
         fixHead =
             (\cs -> if null cs then [] else '/':cs) .
-            takeWhile (\c -> not $ c `elem` pathSeparators)
+            takeWhile (`notElem` pathSeparators)
         mapHead _ [] = []
         mapHead f (x:xs) = f x : xs
         afp = intercalate "/" $ mapHead fixHead ds
         rfp = intercalate "/" ds
-    in DocumentPath $ if abs then "file://" ++ afp else rfp
+    in DocumentPath $ if isAbs then "file://" ++ afp else rfp
 
 -- | Converts a URI string into a 'DocumentPath'.
 uriDocument :: String -> DocumentPath
diff --git a/src/Graphics/QML/Internal/BindCanvas.chs b/src/Graphics/QML/Internal/BindCanvas.chs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/QML/Internal/BindCanvas.chs
@@ -0,0 +1,88 @@
+{-# LANGUAGE
+    ForeignFunctionInterface
+  #-}
+
+module Graphics.QML.Internal.BindCanvas where
+
+{#import Graphics.QML.Internal.BindPrim #}
+
+import Foreign.C.Types
+import Foreign.Ptr
+import Foreign.ForeignPtr.Safe
+import Foreign.Storable
+
+#include "hsqml.h"
+
+--
+-- GL Delegate
+--
+
+{#pointer *HsQMLGLDelegateHandle as ^ foreign newtype #}
+
+type SetupCb = CInt -> CInt -> CInt -> IO ()
+type CleanupCb = IO ()
+type SyncCb = HsQMLJValHandle -> IO CInt
+type PaintCb = Ptr CFloat -> CFloat -> CFloat -> IO ()
+type MakeCb = Ptr (FunPtr SetupCb) -> Ptr (FunPtr CleanupCb) ->
+    Ptr (FunPtr SyncCb) -> Ptr (FunPtr PaintCb) -> IO ()
+type CallbacksFactory = IO (SetupCb, CleanupCb, SyncCb, PaintCb)
+
+{#enum HsQMLGLCanvasType as ^ {underscoreToCase} #}
+
+foreign import ccall "wrapper"
+  marshalSetupCb :: SetupCb -> IO (FunPtr SetupCb)
+
+foreign import ccall "wrapper"
+  marshalCleanupCb :: CleanupCb -> IO (FunPtr CleanupCb)
+
+foreign import ccall "wrapper"  
+  marshalSyncCb :: SyncCb -> IO (FunPtr SyncCb)
+
+foreign import ccall "wrapper"  
+  marshalPaintCb :: PaintCb -> IO (FunPtr PaintCb)
+
+foreign import ccall "wrapper"
+  marshalMakeCb :: MakeCb -> IO (FunPtr MakeCb)
+
+withCallbacksFactory :: CallbacksFactory -> (FunPtr MakeCb -> IO a) -> IO a
+withCallbacksFactory factory with = do
+    let makeFn setupPtrFPtr cleanupPtrFPtr syncPtrFPtr paintPtrFPtr = do
+            (setupFn, cleanupFn, syncFn, paintFn) <- factory
+            setupFPtr <- marshalSetupCb setupFn
+            poke setupPtrFPtr setupFPtr
+            cleanupFPtr <- marshalCleanupCb cleanupFn
+            poke cleanupPtrFPtr cleanupFPtr
+            syncFPtr <- marshalSyncCb syncFn
+            poke syncPtrFPtr syncFPtr
+            paintFPtr <- marshalPaintCb paintFn
+            poke paintPtrFPtr paintFPtr
+    makeFPtr <- marshalMakeCb makeFn
+    with makeFPtr
+
+foreign import ccall "hsqml.h &hsqml_finalise_gldelegate_handle"
+    hsqmlFinaliseGldelegateHandlePtr ::
+        FunPtr (Ptr HsQMLGLDelegateHandle -> IO ())
+
+newGLDelegateHandle :: Ptr HsQMLGLDelegateHandle -> IO HsQMLGLDelegateHandle
+newGLDelegateHandle p = do
+    fp <- newForeignPtr hsqmlFinaliseGldelegateHandlePtr p
+    return $ HsQMLGLDelegateHandle fp
+
+{#fun unsafe hsqml_create_gldelegate as ^
+  {} ->
+  `HsQMLGLDelegateHandle' newGLDelegateHandle* #}
+
+{#fun unsafe hsqml_gldelegate_setup as ^
+  {withHsQMLGLDelegateHandle* `HsQMLGLDelegateHandle',
+   withCallbacksFactory* `CallbacksFactory'} ->
+  `()' #}
+
+{#fun unsafe hsqml_gldelegate_to_jval as ^
+  {withHsQMLGLDelegateHandle* `HsQMLGLDelegateHandle',
+   id `HsQMLJValHandle'} ->
+  `()' #}
+
+{#fun unsafe hsqml_gldelegate_from_jval as ^
+  {withHsQMLGLDelegateHandle* `HsQMLGLDelegateHandle',
+   id `HsQMLJValHandle'} ->
+  `()' #}
diff --git a/src/Graphics/QML/Internal/BindCore.chs b/src/Graphics/QML/Internal/BindCore.chs
--- a/src/Graphics/QML/Internal/BindCore.chs
+++ b/src/Graphics/QML/Internal/BindCore.chs
@@ -9,10 +9,7 @@
 {#import Graphics.QML.Internal.BindObj #}
 
 import Foreign.C.Types
-import Foreign.C.String
-import Foreign.Marshal.Utils
 import Foreign.Ptr
-import Foreign.StablePtr
 
 #include <HsFFI.h>
 
@@ -47,9 +44,6 @@
 withMaybeTrivialCb Nothing = \cont -> cont nullFunPtr
 
 {#enum HsQMLEventLoopStatus as ^ {underscoreToCase} #}
-
-cIntToEnum :: Enum a => CInt -> a
-cIntToEnum = toEnum . fromIntegral
 
 {#fun hsqml_evloop_run as ^
   {withTrivialCb* `TrivialCb',
diff --git a/src/Graphics/QML/Internal/BindObj.chs b/src/Graphics/QML/Internal/BindObj.chs
--- a/src/Graphics/QML/Internal/BindObj.chs
+++ b/src/Graphics/QML/Internal/BindObj.chs
@@ -10,7 +10,7 @@
 import Control.Exception (bracket)
 import Control.Monad (void)
 import Foreign.C.Types
-import Foreign.Marshal.Utils (toBool)
+import Foreign.Marshal.Utils (fromBool, toBool)
 import Foreign.Ptr
 import Foreign.ForeignPtr.Safe
 import Foreign.ForeignPtr.Unsafe
@@ -67,6 +67,9 @@
 foreign import ccall "hsqml.h &hsqml_finalise_object_handle"
   hsqmlFinaliseObjectHandlePtr :: FunPtr (Ptr (HsQMLObjectHandle) -> IO ())
 
+foreign import ccall "hsqml.h &hsqml_finalise_object_weak_handle"
+  hsqmlFinaliseObjectWeakHandlePtr :: FunPtr (Ptr (HsQMLObjectHandle) -> IO ())
+
 newObjectHandle :: Ptr HsQMLObjectHandle -> IO HsQMLObjectHandle
 newObjectHandle p = do
   fp <- newForeignPtr hsqmlFinaliseObjectHandlePtr p
@@ -83,7 +86,7 @@
 
 {#fun unsafe hsqml_object_set_active as ^
   {withMaybeHsQMLObjectHandle* `Maybe HsQMLObjectHandle'} ->
- `Bool' toBool #}
+  `Bool' toBool #}
 
 withActiveObject :: HsQMLObjectHandle -> IO () -> IO ()
 withActiveObject hndl action =
@@ -116,8 +119,49 @@
   {id `HsQMLJValHandle'} ->
   `HsQMLObjectHandle' newObjectHandle* #}
 
+{#fun unsafe hsqml_object_reference_handle as ^
+  {id `Ptr HsQMLObjectHandle',
+   fromBool `Bool'} ->
+  `()' #}
+
+copyObjectHandle :: HsQMLObjectHandle -> Bool -> IO HsQMLObjectHandle
+copyObjectHandle (HsQMLObjectHandle fp) weak = do
+    withForeignPtr fp $ \p -> do
+        hsqmlObjectReferenceHandle p weak
+        fp' <- newForeignPtr final p
+        return $ HsQMLObjectHandle fp'
+    where final = if weak
+                  then hsqmlFinaliseObjectWeakHandlePtr
+                  else hsqmlFinaliseObjectHandlePtr
+
 {#fun hsqml_fire_signal as ^
   {withHsQMLObjectHandle* `HsQMLObjectHandle',
    `Int',
    id `Ptr (Ptr ())'} ->
   `()' #}
+
+{#pointer *HsQMLObjFinaliserHandle as ^ foreign newtype #}
+
+foreign import ccall "hsqml.h &hsqml_finalise_obj_finaliser"
+  hsqmlFinaliseObjFinaliserPtr :: FunPtr (Ptr HsQMLObjFinaliserHandle -> IO ())
+
+type ObjFinaliserFunc = Ptr HsQMLObjectHandle -> IO ()
+
+foreign import ccall "wrapper"  
+  marshalObjFinaliser :: ObjFinaliserFunc -> IO (FunPtr ObjFinaliserFunc)
+
+newObjFinaliserHandle ::
+    Ptr HsQMLObjFinaliserHandle -> IO HsQMLObjFinaliserHandle
+newObjFinaliserHandle p = do
+    fp <- newForeignPtr hsqmlFinaliseObjFinaliserPtr p
+    return $ HsQMLObjFinaliserHandle fp
+
+{#fun unsafe hsqml_create_obj_finaliser as ^
+  {id `FunPtr ObjFinaliserFunc'} ->
+  `HsQMLObjFinaliserHandle' newObjFinaliserHandle* #}
+
+{#fun unsafe hsqml_object_add_finaliser as ^
+  {withHsQMLObjectHandle* `HsQMLObjectHandle',
+   withHsQMLObjFinaliserHandle* `HsQMLObjFinaliserHandle'} ->
+  `()' #}
+
diff --git a/src/Graphics/QML/Internal/BindPrim.chs b/src/Graphics/QML/Internal/BindPrim.chs
--- a/src/Graphics/QML/Internal/BindPrim.chs
+++ b/src/Graphics/QML/Internal/BindPrim.chs
@@ -12,8 +12,8 @@
 
 #include "hsqml.h"
 
-cIntConv :: (Integral a, Integral b) => a -> b
-cIntConv = fromIntegral
+cIntToEnum :: Enum a => CInt -> a
+cIntToEnum = toEnum . fromIntegral
 
 --
 -- String
diff --git a/src/Graphics/QML/Internal/Marshal.hs b/src/Graphics/QML/Internal/Marshal.hs
--- a/src/Graphics/QML/Internal/Marshal.hs
+++ b/src/Graphics/QML/Internal/Marshal.hs
@@ -34,7 +34,7 @@
 tyString  = TypeId 10
 tyObject  = TypeId 39
 tyVoid    = TypeId 43
-tyJSValue = TypeId $ hsqmlJValTypeId
+tyJSValue = TypeId hsqmlJValTypeId
 
 type MTypeCValFunc t = Tagged t TypeId
 type MFromCValFunc t = Ptr () -> ErrIO t
@@ -83,6 +83,14 @@
 type instance ModeFrom IIsObjType = No
 type instance ModeFrom IGetObjType = No
 
+-- | 'MarshalMode' for non-object types with to-only marshalling.
+type family ModeTo c
+type instance ModeTo ICanGetFrom = No
+type instance ModeTo ICanPassTo = Yes
+type instance ModeTo ICanReturnTo = Yes
+type instance ModeTo IIsObjType = No
+type instance ModeTo IGetObjType = No
+
 -- | 'MarshalMode' for void in method returns.
 type family ModeRetVoid c
 type instance ModeRetVoid ICanGetFrom = No
@@ -107,6 +115,14 @@
 type instance ModeObjFrom a IIsObjType = Yes
 type instance ModeObjFrom a IGetObjType = a
 
+-- | 'MarshalMode' for object types with to-only marshalling.
+type family ModeObjTo a c
+type instance ModeObjTo a ICanGetFrom = No
+type instance ModeObjTo a ICanPassTo = Yes
+type instance ModeObjTo a ICanReturnTo = Yes
+type instance ModeObjTo a IIsObjType = Yes
+type instance ModeObjTo a IGetObjType = a
+
 -- | Type value indicating a capability is supported.
 data Yes
 
@@ -191,35 +207,35 @@
 mToHndl = mToHndl_ (marshaller :: MarshallerFor t)
 
 unimplFromCVal :: MFromCValFunc t
-unimplFromCVal = \_ -> error "Type does not support mFromCVal."
+unimplFromCVal _ = error "Type does not support mFromCVal."
 
 unimplToCVal :: MToCValFunc t
-unimplToCVal = \_ _ -> error "Type does not support mToCVal."
+unimplToCVal _ _ = error "Type does not support mToCVal."
 
 unimplWithCVal :: MWithCValFunc t
-unimplWithCVal = \_ _ -> error "Type does not support mWithCVal."
+unimplWithCVal _ _ = error "Type does not support mWithCVal."
 
 unimplFromJVal :: MFromJValFunc t
-unimplFromJVal = \_ -> error "Type does not support mFromJVal."
+unimplFromJVal _ = error "Type does not support mFromJVal."
 
 unimplWithJVal :: MWithJValFunc t
-unimplWithJVal = \_ _ -> error "Type does not support mWithJVal."
+unimplWithJVal _ _ = error "Type does not support mWithJVal."
 
 unimplFromHndl :: MFromHndlFunc t
-unimplFromHndl = \_ -> error "Type does not support mFromHndl."
+unimplFromHndl _ = error "Type does not support mFromHndl."
 
 unimplToHndl :: MToHndlFunc t
-unimplToHndl = \_ -> error "Type does not support mToHndl."
+unimplToHndl _ = error "Type does not support mToHndl."
 
 jvalFromCVal :: (Marshal t) => MFromCValFunc t
 jvalFromCVal = mFromJVal . HsQMLJValHandle . castPtr
 
 jvalToCVal :: (Marshal t) => MToCValFunc t
-jvalToCVal = \val ptr -> mWithJVal val $ \jval ->
+jvalToCVal val ptr = mWithJVal val $ \jval ->
     hsqmlSetJval (HsQMLJValHandle $ castPtr ptr) jval
 
 jvalWithCVal :: (Marshal t) => MWithCValFunc t
-jvalWithCVal = \val f -> mWithJVal val $ \(HsQMLJValHandle ptr) ->
+jvalWithCVal val f = mWithJVal val $ \(HsQMLJValHandle ptr) ->
     f $ castPtr ptr
 
 instance Marshal () where
diff --git a/src/Graphics/QML/Internal/MetaObj.hs b/src/Graphics/QML/Internal/MetaObj.hs
--- a/src/Graphics/QML/Internal/MetaObj.hs
+++ b/src/Graphics/QML/Internal/MetaObj.hs
@@ -1,11 +1,9 @@
 module Graphics.QML.Internal.MetaObj where
 
-import Graphics.QML.Internal.BindObj
-import Graphics.QML.Internal.Marshal
 import Graphics.QML.Internal.Types
 
 import Control.Monad
-import Control.Monad.Trans.State
+import Control.Monad.Trans.State (State, execState, get, put)
 import Data.Bits
 import Data.Map (Map)
 import qualified Data.Map as Map
@@ -37,8 +35,8 @@
 crlAppend :: CRList a -> [a] -> CRList a
 crlAppend (CRList n xs) ys = CRList n' xs'
   where (xs', n')       = rev ys xs n
-        rev []     vs n = (vs, n)
-        rev (u:us) vs n = rev us (u:vs) (n+1)
+        rev []     vs m = (vs, m)
+        rev (u:us) vs m = rev us (u:vs) (m+1)
 
 crlToNewArray :: (Storable b) => (a -> IO b) -> CRList a -> IO (Ptr b)
 crlToNewArray f (CRList len lst) = do
@@ -124,8 +122,7 @@
   in enc
 
 filterMembers :: MemberKind -> [Member tt] -> [Member tt]
-filterMembers k ms =
-  filter (\m -> k == memberKind m) ms
+filterMembers k = filter (\m -> k == memberKind m)
 
 newMOCState :: MOCState -> MOCState
 newMOCState enc = MOCState
@@ -149,12 +146,12 @@
   let msChr = mStrChar state
       msInf = mStrInfo state
       msMap = mStrMap state
-  case (Map.lookup str msMap) of
+  case Map.lookup str msMap of
     Just idx -> writeInt idx
     Nothing  -> do
       let idx = crlLen msInf - 1
-          msChr' = msChr `crlAppend` (map castCharToCChar str) `crlAppend1` 0
-          msInf' = msInf `crlAppend1` (fromIntegral $ crlLen msChr')
+          msChr' = msChr `crlAppend` map castCharToCChar str `crlAppend1` 0
+          msInf' = msInf `crlAppend1` fromIntegral (crlLen msChr')
           msMap' = Map.insert str (fromIntegral idx) msMap
       put $ state {
         mStrChar = msChr',
@@ -168,15 +165,15 @@
   let types = memberTypes m
       datal = mData state
       mpMap = mParamMap state
-  case (Map.lookup types mpMap) of
-    Just idx -> return ()
+  case Map.lookup types mpMap of
+    Just _ -> return ()
     Nothing  -> do
       let idx = crlLen datal
           mpMap' = Map.insert types (fromIntegral idx) mpMap
       put $ state {
         mParamMap = mpMap'}
-      mapM_ writeInt $ map typeId types
-      mapM_ writeString $ replicate (length $ memberParams m) ""
+      mapM_ (writeInt . typeId) types
+      replicateM_ (length $ memberParams m) $ writeString ""
 
 writeMethod :: Member tt -> State MOCState ()
 writeMethod m = do
@@ -193,8 +190,8 @@
   state <- get
   put $ state {
     mDataMethodsIdx = mplus (mDataMethodsIdx state) (Just idx),
-    mMethodCount = mc + (mMethodCount state),
-    mSignalCount = sc + (mSignalCount state),
+    mMethodCount = mc + mMethodCount state,
+    mSignalCount = sc + mSignalCount state,
     mSigMap = maybe (mSigMap state) (\k ->
       Map.insert k (fromIntegral $ mSignalCount state) (mSigMap state)) $
       memberKey m,
@@ -207,13 +204,13 @@
   writeString $ memberName p
   writeInt $ typeId $ memberType p
   writeInt (pfReadable .|. pfScriptable .|.
-    (if (ConstPropertyMember == memberKind p) then pfConstant else 0) .|.
-    (if (isJust $ memberFunAux p) then pfWritable else 0) .|.
-    (if (isJust $ memberKey p) then pfNotify else 0))
+    (if ConstPropertyMember == memberKind p then pfConstant else 0) .|.
+    (if isJust (memberFunAux p) then pfWritable else 0) .|.
+    (if isJust (memberKey p) then pfNotify else 0))
   state <- get
   put $ state {
     mDataPropsIdx = mplus (mDataPropsIdx state) (Just idx),
-    mPropertyCount = 1 + (mPropertyCount state),
+    mPropertyCount = 1 + mPropertyCount state,
     mFuncProperties = mFuncProperties state
       `crlAppend1` (Just $ memberFun p) `crlAppend1` memberFunAux p
   }
diff --git a/src/Graphics/QML/Internal/Objects.hs b/src/Graphics/QML/Internal/Objects.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/QML/Internal/Objects.hs
@@ -0,0 +1,78 @@
+{-# LANGUAGE
+    ScopedTypeVariables,
+    TypeFamilies
+  #-}
+
+module Graphics.QML.Internal.Objects where
+
+import Graphics.QML.Internal.BindObj
+import Graphics.QML.Internal.Marshal
+import Graphics.QML.Internal.Types
+
+import Control.Monad.Trans.Maybe
+import Data.Tagged
+import Data.Typeable
+import Foreign.ForeignPtr
+
+-- | Represents an instance of the QML class which wraps the type @tt@.
+newtype ObjRef tt = ObjRef HsQMLObjectHandle
+
+instance (Typeable tt) => Marshal (ObjRef tt) where
+    type MarshalMode (ObjRef tt) c d = ModeObjBidi tt c
+    marshaller = Marshaller {
+        mTypeCVal_ = retag (mTypeCVal :: Tagged AnyObjRef TypeId),
+        mFromCVal_ = \ptr -> do
+            anyObj <- mFromCVal ptr
+            MaybeT $ fromAnyObjRefIO anyObj,
+        mToCVal_ = \(ObjRef hndl) ptr ->
+            mToCVal (AnyObjRef hndl) ptr,
+        mWithCVal_ = \(ObjRef hndl) f ->
+            mWithCVal (AnyObjRef hndl) f,
+        mFromJVal_ = \ptr -> do
+            anyObj <- mFromJVal ptr
+            MaybeT $ fromAnyObjRefIO anyObj,
+        mWithJVal_ = \(ObjRef hndl) f ->
+            mWithJVal (AnyObjRef hndl) f,
+        mFromHndl_ =
+            return . ObjRef,
+        mToHndl_ = \(ObjRef hndl) ->
+            return hndl}
+
+fromObjRefIO :: ObjRef tt -> IO tt
+fromObjRefIO (ObjRef hndl) = hsqmlObjectGetHsValue hndl
+
+-- | Represents an instance of a QML class which wraps an arbitrary Haskell
+-- type. Unlike 'ObjRef', an 'AnyObjRef' only carries the type of its Haskell
+-- value dynamically and does not encode it into the static type.
+newtype AnyObjRef = AnyObjRef HsQMLObjectHandle
+
+instance Marshal AnyObjRef where
+    type MarshalMode AnyObjRef c d = ModeObjBidi No c
+    marshaller = Marshaller {
+        mTypeCVal_ = Tagged tyJSValue,
+        mFromCVal_ = jvalFromCVal,
+        mToCVal_ = jvalToCVal,
+        mWithCVal_ = jvalWithCVal,
+        mFromJVal_ = \ptr -> MaybeT $ do
+            hndl <- hsqmlGetObjectFromJval ptr
+            return $ if isNullObjectHandle hndl
+                then Nothing else Just $ AnyObjRef hndl,
+        mWithJVal_ = \(AnyObjRef hndl@(HsQMLObjectHandle ptr)) f -> do
+            jval <- hsqmlObjectGetJval hndl
+            ret <- f jval
+            touchForeignPtr ptr
+            return ret,
+        mFromHndl_ =
+            return . AnyObjRef,
+        mToHndl_ = \(AnyObjRef hndl) ->
+            return hndl}
+
+fromAnyObjRefIO :: forall tt. (Typeable tt) =>
+    AnyObjRef -> IO (Maybe (ObjRef tt))
+fromAnyObjRefIO (AnyObjRef hndl) = do
+    info <- hsqmlObjectGetHsTyperep hndl
+    let srcRep = typeOf (undefined :: tt)
+        dstRep = cinfoObjType info
+    return $ if srcRep == dstRep
+        then Just $ ObjRef hndl
+        else Nothing
diff --git a/src/Graphics/QML/Internal/Types.hs b/src/Graphics/QML/Internal/Types.hs
--- a/src/Graphics/QML/Internal/Types.hs
+++ b/src/Graphics/QML/Internal/Types.hs
@@ -1,7 +1,6 @@
 module Graphics.QML.Internal.Types where
 
 import Data.Map (Map)
-import qualified Data.Map as Map
 import Data.Typeable
 import Data.Unique
 import Foreign.Ptr
diff --git a/src/Graphics/QML/Marshal.hs b/src/Graphics/QML/Marshal.hs
--- a/src/Graphics/QML/Marshal.hs
+++ b/src/Graphics/QML/Marshal.hs
@@ -12,9 +12,11 @@
     marshaller),
   ModeBidi,
   ModeFrom,
+  ModeTo,
   ModeRetVoid,
   ModeObjBidi,
   ModeObjFrom,
+  ModeObjTo,
   Yes,
   CanGetFrom,
   ICanGetFrom,
@@ -28,11 +30,17 @@
   IGetObjType,
   Marshaller,
 
+  -- * Data Types
+  Ignored (
+    Ignored),
+
   -- * Custom Marshallers
   bidiMarshallerIO,
   bidiMarshaller,
   fromMarshallerIO,
   fromMarshaller,
+  toMarshallerIO,
+  toMarshaller
 ) where
 
 import Graphics.QML.Internal.BindPrim
@@ -41,14 +49,11 @@
 
 import Control.Monad
 import Control.Monad.Trans.Maybe
-import Data.Maybe
 import Data.Tagged
 import Data.Int
 import Data.Text (Text)
-import qualified Data.Text as T
 import qualified Data.Text.Foreign as T
 import Foreign.C.Types
-import Foreign.C.String
 import Foreign.Marshal.Alloc
 import Foreign.Ptr
 import Foreign.Storable
@@ -217,6 +222,26 @@
         mFromHndl_ = unimplFromHndl,
         mToHndl_ = unimplToHndl}
 
+--
+-- Ignored
+--
+
+-- | Represents an argument whose value is ignored.
+
+data Ignored = Ignored
+
+instance Marshal Ignored where
+    type MarshalMode Ignored c d = ModeFrom c
+    marshaller = Marshaller {
+        mTypeCVal_ = Tagged tyJSValue,
+        mFromCVal_ = jvalFromCVal,
+        mToCVal_ = unimplToCVal,
+        mWithCVal_ = unimplWithCVal,
+        mFromJVal_ = \_ -> MaybeT . return $ Just Ignored,
+        mWithJVal_ = unimplWithJVal,
+        mFromHndl_ = unimplFromHndl,
+        mToHndl_ = unimplToHndl}
+
 type BidiMarshaller a b = Marshaller b
     (MarshalMode a ICanGetFrom ())
     (MarshalMode a ICanPassTo ())
@@ -279,3 +304,34 @@
     forall a b. (Marshal a, CanGetFrom a ~ Yes) =>
     (a -> b) -> FromMarshaller a b
 fromMarshaller fromFn = fromMarshallerIO (return . fromFn)
+
+type ToMarshaller a b = Marshaller b
+    No
+    (MarshalMode a ICanPassTo ())
+    (MarshalMode a ICanReturnTo ())
+    (MarshalMode a IIsObjType ())
+    (MarshalMode a IGetObjType ())
+
+-- | Provides a "to" 'Marshaller' which allows you to define an instance of
+-- 'Marshal' for your own type @b@ in terms of another marshallable type @a@.
+-- Type @b@ should have a 'MarshalMode' of 'ModeObjTo' or 'ModeTo'
+-- depending on whether @a@ was an object type or not.
+toMarshallerIO ::
+    forall a b. (Marshal a, CanPassTo a ~ Yes) =>
+    (b -> IO a) -> ToMarshaller a b
+toMarshallerIO toFn = Marshaller {
+    mTypeCVal_ = retag (mTypeCVal :: Tagged a TypeId),
+    mFromCVal_ = unimplFromCVal,
+    mToCVal_ = \val ptr -> flip mToCVal ptr =<< toFn val,
+    mWithCVal_ = \val f -> flip mWithCVal f =<< toFn val,
+    mFromJVal_ = unimplFromJVal,
+    mWithJVal_ = \val f -> flip mWithJVal f =<< toFn val,
+    mFromHndl_ = unimplFromHndl,
+    mToHndl_ = \val -> mToHndl =<< toFn val}
+
+-- | Variant of 'toMarshallerIO' where the conversion function between types
+-- @a@ and @b@ does not live in the IO monad.
+toMarshaller ::
+    forall a b. (Marshal a, CanPassTo a ~ Yes) =>
+    (b -> a) -> ToMarshaller a b
+toMarshaller toFn = toMarshallerIO (return . toFn)
diff --git a/src/Graphics/QML/Objects.hs b/src/Graphics/QML/Objects.hs
--- a/src/Graphics/QML/Objects.hs
+++ b/src/Graphics/QML/Objects.hs
@@ -54,20 +54,17 @@
   defPropertySigRW'
 ) where
 
-import System.IO
-
 import Graphics.QML.Internal.BindCore
 import Graphics.QML.Internal.BindObj
 import Graphics.QML.Internal.JobQueue
 import Graphics.QML.Internal.Marshal
 import Graphics.QML.Internal.MetaObj
+import Graphics.QML.Internal.Objects
 import Graphics.QML.Internal.Types
 
 import Control.Concurrent.MVar
-import Control.Monad.Trans.Maybe
 import Data.Map (Map)
 import qualified Data.Map as Map
-import Data.Set (Set)
 import qualified Data.Set as Set
 import Data.Maybe
 import Data.Proxy
@@ -76,9 +73,7 @@
 import Data.IORef
 import Data.Unique
 import Foreign.Ptr
-import Foreign.ForeignPtr
 import Foreign.Storable
-import Foreign.Marshal.Alloc
 import Foreign.Marshal.Array
 import System.IO.Unsafe
 import Numeric
@@ -87,32 +82,6 @@
 -- ObjRef
 --
 
--- | Represents an instance of the QML class which wraps the type @tt@.
-data ObjRef tt = ObjRef {
-  objHndl :: HsQMLObjectHandle
-}
-
-instance (Typeable tt) => Marshal (ObjRef tt) where
-    type MarshalMode (ObjRef tt) c d = ModeObjBidi tt c
-    marshaller = Marshaller {
-        mTypeCVal_ = retag (mTypeCVal :: Tagged AnyObjRef TypeId),
-        mFromCVal_ = \ptr -> do
-            any <- mFromCVal ptr
-            MaybeT $ return $ fromAnyObjRef any,
-        mToCVal_ = \obj ptr ->
-            mToCVal (AnyObjRef $ objHndl obj) ptr,
-        mWithCVal_ = \obj f ->
-            mWithCVal (AnyObjRef $ objHndl obj) f,
-        mFromJVal_ = \ptr -> do
-            any <- mFromJVal ptr
-            MaybeT $ return $ fromAnyObjRef any,
-        mWithJVal_ = \obj f ->
-            mWithJVal (AnyObjRef $ objHndl obj) f,
-        mFromHndl_ = \hndl ->
-            return $ ObjRef hndl,
-        mToHndl_ = \obj ->
-            return $ objHndl obj}
-
 -- | Creates a QML object given a 'Class' and a Haskell value of type @tt@.
 newObject :: forall tt. Class tt -> tt -> IO (ObjRef tt)
 newObject (Class cHndl) obj =
@@ -130,37 +99,6 @@
 fromObjRef :: ObjRef tt -> tt
 fromObjRef = unsafeDupablePerformIO . fromObjRefIO
 
-fromObjRefIO :: ObjRef tt -> IO tt
-fromObjRefIO = hsqmlObjectGetHsValue . objHndl
-
--- | Represents an instance of a QML class which wraps an arbitrary Haskell
--- type. Unlike 'ObjRef', an 'AnyObjRef' only carries the type of its Haskell
--- value dynamically and does not encode it into the static type.
-data AnyObjRef = AnyObjRef {
-  anyObjHndl :: HsQMLObjectHandle
-}
-
-instance Marshal AnyObjRef where
-    type MarshalMode AnyObjRef c d = ModeObjBidi No c
-    marshaller = Marshaller {
-        mTypeCVal_ = Tagged tyJSValue,
-        mFromCVal_ = jvalFromCVal,
-        mToCVal_ = jvalToCVal,
-        mWithCVal_ = jvalWithCVal,
-        mFromJVal_ = \ptr -> MaybeT $ do
-            hndl <- hsqmlGetObjectFromJval ptr
-            return $ if isNullObjectHandle hndl
-                then Nothing else Just $ AnyObjRef hndl,
-        mWithJVal_ = \(AnyObjRef hndl@(HsQMLObjectHandle ptr)) f -> do
-            jval <- hsqmlObjectGetJval hndl
-            ret <- f jval
-            touchForeignPtr ptr
-            return ret,
-        mFromHndl_ = \hndl ->
-            return $ AnyObjRef hndl,
-        mToHndl_ = \obj ->
-            return $ anyObjHndl obj}
-
 -- | Upcasts an 'ObjRef' into an 'AnyObjRef'.
 anyObjRef :: ObjRef tt -> AnyObjRef
 anyObjRef (ObjRef hndl) = AnyObjRef hndl
@@ -170,24 +108,12 @@
 fromAnyObjRef :: (Typeable tt) => AnyObjRef -> Maybe (ObjRef tt)
 fromAnyObjRef = unsafeDupablePerformIO . fromAnyObjRefIO
 
-fromAnyObjRefIO :: forall tt. (Typeable tt) =>
-    AnyObjRef -> IO (Maybe (ObjRef tt))
-fromAnyObjRefIO (AnyObjRef hndl) = do
-    info <- hsqmlObjectGetHsTyperep hndl
-    let srcRep = typeOf (undefined :: tt)
-        dstRep = cinfoObjType info
-    if srcRep == dstRep
-        then return $ Just $ ObjRef hndl
-        else return Nothing
-
 --
 -- Class
 --
 
 -- | Represents a QML class which wraps the type @tt@.
-data Class tt = Class {
-  classHndl :: HsQMLClassHandle
-}
+newtype Class tt = Class HsQMLClassHandle
 
 -- | Creates a new QML class for the type @tt@.
 newClass :: forall tt. (Typeable tt) => [Member tt] -> IO (Class tt)
@@ -230,7 +156,7 @@
             (\_ _ -> return ())
             Nothing
             (Just k)
-    in map (uncurry impMember) $ zip [0..] impKeys
+    in map (uncurry impMember) $ zip [(0::Int)..] impKeys
 
 --
 -- Default Class
@@ -328,7 +254,7 @@
 newtype VoidIO = VoidIO {runVoidIO :: (IO ())}
 
 instance MethodSuffix VoidIO where
-    mkMethodFunc _ f pv = errIO $ runVoidIO f
+    mkMethodFunc _ f _ = errIO $ runVoidIO f
     mkMethodTypes = Tagged $ MethodTypeInfo [] tyVoid
 
 class IsVoidIO a
diff --git a/src/Graphics/QML/Objects/Weak.hs b/src/Graphics/QML/Objects/Weak.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/QML/Objects/Weak.hs
@@ -0,0 +1,120 @@
+-- | Facilities for working with weak references, finalisers, and factory
+-- pools.
+module Graphics.QML.Objects.Weak (
+    -- * Weak Object References
+    WeakObjRef,
+    toWeakObjRef,
+    fromWeakObjRef,
+
+    -- * Object Finalisers
+    ObjFinaliser,
+    newObjFinaliser,
+    addObjFinaliser,
+
+    -- * Factory Pools
+    FactoryPool,
+    newFactoryPool,
+    getPoolObject
+) where
+
+import Graphics.QML.Internal.BindObj
+import Graphics.QML.Internal.Objects
+
+import Control.Concurrent.MVar
+import qualified Data.Map as Map
+import Data.Map (Map)
+
+-- | Represents a weak reference to a QML object which wraps the type @tt@.
+--
+-- Unlike ordinary strong references, a weak reference does not prevent the
+-- QML garbage collector from collecting the underlying object. Weak references
+-- can be used to monitor the life cycles of QML objects.
+newtype WeakObjRef tt = WeakObjRef HsQMLObjectHandle
+
+-- | Converts a strong 'ObjRef' into a 'WeakObjRef'. 
+toWeakObjRef :: ObjRef tt -> IO (WeakObjRef tt)
+toWeakObjRef (ObjRef hndl) = do
+    hndl' <- copyObjectHandle hndl True
+    return $ WeakObjRef hndl'
+
+-- | Converts a 'WeakObjRef' into a strong 'ObjRef'.
+--
+-- If the underlying QML object has already been collected then the resulting
+-- 'ObjRef' can be used to reincarnate it.
+fromWeakObjRef :: WeakObjRef tt -> IO (ObjRef tt)
+fromWeakObjRef (WeakObjRef hndl) = do
+    hndl' <- copyObjectHandle hndl False
+    return $ ObjRef hndl'
+
+-- | Represents an object finaliser function for QML objects which wrap the
+-- type @tt@.
+data ObjFinaliser tt = ObjFinaliser HsQMLObjFinaliserHandle
+
+-- | Create a new object finaliser from a finaliser function.
+--
+-- Note that at the time the finaliser is called the runtime will have already
+-- comitted to collecting the underlying QML object. The 'ObjRef' passed into
+-- the finaliser can be used to reincarnate the object, but this QML object
+-- will have a distinct identity to the original.
+newObjFinaliser :: (ObjRef tt -> IO ()) -> IO (ObjFinaliser tt)
+newObjFinaliser f = do
+    fPtr <- marshalObjFinaliser $ \hPtr -> do
+        hndl <- newObjectHandle hPtr
+        f $ ObjRef hndl
+    final <- hsqmlCreateObjFinaliser fPtr
+    return $ ObjFinaliser final
+
+-- | Adds an object finaliser to an QML object.
+--
+-- The finaliser will be called no more than once for each time it was added to
+-- an object. The timing of finaliser execution is subject to the combined
+-- behaviour of the Haskell and QML garbage collectors. All outstanding
+-- finalisers will be run when the QML engine is terminated provided that the
+-- program does not prematurely exit.
+addObjFinaliser :: ObjFinaliser tt -> ObjRef tt -> IO ()
+addObjFinaliser (ObjFinaliser final) (ObjRef hndl) =
+    hsqmlObjectAddFinaliser hndl final
+
+-- | Represents an object factory which maintains a one-to-one mapping between
+-- values of type @tt@ and QML object instances.
+--
+-- 'ObjRef's manufactured by the pool are cached using the wrapped type @tt@ as
+-- the lookup key in an ordered map. The pool uses weak references to
+-- automatically purge objects which no longer have any strong references
+-- leading to them from either Haskell or QML code.
+
+-- Hence, the pool guarantees that if QML code is using a pool object (e.g. as
+-- a source for data binding) then the same object instance can be obtained
+-- again from the pool. Conversely, if an object instance is no longer being
+-- used then pool will not prevent it from being garbage collected.
+data FactoryPool tt = FactoryPool {
+    factory_   :: tt -> IO (ObjRef tt),
+    pool_      :: MVar (Map tt (WeakObjRef tt)),
+    finaliser_ :: ObjFinaliser tt
+}
+
+-- | Creates a new 'FactoryPool' using the supplied factory function.
+newFactoryPool :: (Ord tt) =>
+    (tt -> IO (ObjRef tt)) -> IO (FactoryPool tt)
+newFactoryPool factory = do
+    pool <- newMVar Map.empty
+    finaliser <- newObjFinaliser $ \obj -> do
+        value <- fromObjRefIO obj
+        modifyMVar_ pool (return . Map.delete value)
+    return $ FactoryPool factory pool finaliser
+
+-- | Return the pool's canonical QML object for a value of @tt@, either by
+-- creating it or looking it up in the pool's cache of objects.
+getPoolObject :: (Ord tt) =>
+    FactoryPool tt -> tt -> IO (ObjRef tt)
+getPoolObject (FactoryPool factory pool finaliser) value =
+    modifyMVar pool $ \pmap ->
+        case Map.lookup value pmap of
+            Just wkObj -> do
+                obj <- fromWeakObjRef wkObj
+                return (pmap, obj)
+            Nothing  -> do
+                obj <- factory value
+                addObjFinaliser finaliser obj
+                wkObj <- toWeakObjRef obj
+                return (Map.insert value wkObj pmap, obj)
