function App3D(dom, appNS) {

    appNS = appNS || {};
    appNS.graphic = appNS.graphic || {};

    if (appNS.autoRender == null) {
        appNS.autoRender = true;
    }

    if (typeof dom === 'string') {
        dom = document.querySelector(dom);
    }

    if (!dom) { throw new Error('Invalid dom'); }

    var isDomCanvas = !dom.nodeName  // Not in dom environment
        || dom.nodeName.toUpperCase() === 'CANVAS';

    var rendererOpts = {};
    isDomCanvas && (rendererOpts.canvas = dom);
    appNS.devicePixelRatio && (rendererOpts.devicePixelRatio = appNS.devicePixelRatio);

    var gRenderer = new Renderer(rendererOpts);
    var gWidth = appNS.width || dom.clientWidth;
    var gHeight = appNS.height || dom.clientHeight;

    var gScene = new Scene();
    var gTimeline = new Timeline();
    var gShadowPass = appNS.graphic.shadow && new ShadowMapPass();
    var gRayPicking = appNS.event && new RayPicking({
        scene: gScene,
        renderer: gRenderer
    });

    !isDomCanvas && dom.appendChild(gRenderer.canvas);

    gRenderer.resize(gWidth, gHeight);

    var gFrameTime = 0;
    var gElapsedTime = 0;

    gTimeline.start();

    var userMethods = {};
    for (var key in appNS.methods) {
        userMethods[key] = appNS.methods[key].bind(appNS, this);
    }

    Object.defineProperties(this, {
        /**
         * Container dom element
         * @name clay.application.App3D#container
         * @type {HTMLElement}
         */
        container: { get: function () { return dom; } },
        /**
         * @name clay.application.App3D#renderer
         * @type {clay.Renderer}
         */
        renderer: { get: function () { return gRenderer; }},
        /**
         * @name clay.application.App3D#scene
         * @type {clay.Renderer}
         */
        scene: { get: function () { return gScene; }},
        /**
         * @name clay.application.App3D#timeline
         * @type {clay.Renderer}
         */
        timeline: { get: function () { return gTimeline; }},
        /**
         * Time elapsed since last frame. Can be used in loop to calculate the movement.
         * @name clay.application.App3D#frameTime
         * @type {number}
         */
        frameTime: { get: function () { return gFrameTime; }},
        /**
         * Time elapsed since application created.
         * @name clay.application.App3D#elapsedTime
         * @type {number}
         */
        elapsedTime: { get: function () { return gElapsedTime; }},

        /**
         * Width of viewport.
         * @name clay.application.App3D#width
         * @type {number}
         */
        width: { get: function () { return gRenderer.getWidth(); }},
        /**
         * Height of viewport.
         * @name clay.application.App3D#height
         * @type {number}
         */
        height: { get: function () { return gRenderer.getHeight(); }},

        /**
         * Methods from {@link clay.application.create}
         * @name clay.application.App3D#methods
         * @type {number}
         */
        methods: { get: function () { return userMethods; } },

        _shadowPass: { get: function () { return gShadowPass; } },

        _appNS: { get: function () { return appNS; } },
    });

    /**
     * Resize the application. Will use the container clientWidth/clientHeight if width/height in parameters are not given.
     * @function
     * @memberOf {clay.application.App3D}
     * @param {number} [width]
     * @param {number} [height]
     */
    this.resize = function (width, height) {
        gWidth = width || appNS.width || dom.clientWidth;
        gHeight = height || dom.height || dom.clientHeight;
        gRenderer.resize(gWidth, gHeight);
    };

    /**
     * Dispose the application
     * @function
     */
    this.dispose = function () {
        this._disposed = true;

        if (appNS.dispose) {
            appNS.dispose(this);
        }
        gTimeline.stop();
        gRenderer.disposeScene(gScene);
        gShadowPass && gShadowPass.dispose(gRenderer);

        dom.innerHTML = '';
        EVE_NAMES.forEach(function (eveType) {
            this[makeHandlerName(eveType)] && vendor.removeEventListener(dom, makeHandlerName(eveType));
        }, this);
    };

    gRayPicking && this._initMouseEvents(gRayPicking);

    this._geoCache = new LRU$1(20);
    this._texCache = new LRU$1(20);

    // GPU Resources.
    this._texturesList = {};
    this._geometriesList = {};

    // Do init the application.
    var initPromise = Promise.resolve(appNS.init && appNS.init(this));
    // Use the inited camera.
    gRayPicking && (gRayPicking.camera = gScene.getMainCamera());

    if (!appNS.loop) {
        console.warn('Miss loop method.');
    }

    var self = this;
    initPromise.then(function () {
        gTimeline.on('frame', function (frameTime) {
            gFrameTime = frameTime;
            gElapsedTime += frameTime;

            var camera = gScene.getMainCamera();
            if (camera) {
                camera.aspect = gRenderer.getViewportAspect();
            }
            gRayPicking && (gRayPicking.camera = camera);

            appNS.loop && appNS.loop(self);

            if (appNS.autoRender) {
                self.render();
            }

            self.collectResources();
        }, this);
    });

    gScene.on('beforerender', function (renderer, scene, camera, renderList) {
        if (this._inRender) {
            // Only update graphic options when using #render function.
            this._updateGraphicOptions(appNS.graphic, renderList.opaque, false);
            this._updateGraphicOptions(appNS.graphic, renderList.transparent, false);
        }
    }, this);
}
Using App3D is a much more convenient way to create and manage your 3D application.

It provides the abilities to:

+ Manage application loop and rendering.
+ Collect GPU resource automatically without memory leak concern.
+ Mouse event management.
+ Create scene objects, materials, textures with simpler code.
+ Load models with one line of code.
+ Promised interfaces.

Here is a basic example to create a rotating cube.

```js
var app = clay.application.create('#viewport', {
    init: function (app) {
        // Create a perspective camera.
        // First parameter is the camera position. Which is in front of the cube.
        // Second parameter is the camera lookAt target. Which is the origin of the world, and where the cube puts.
        this._camera = app.createCamera([0, 2, 5], [0, 0, 0]);
        // Create a sample cube
        this._cube = app.createCube();
        // Create a directional light. The direction is from top right to left bottom, away from camera.
        this._mainLight = app.createDirectionalLight([-1, -1, -1]);
    },
    loop: function (app) {
        // Simply rotating the cube every frame.
        this._cube.rotation.rotateY(app.frameTime / 1000);
    }
});
```
@constructor
@alias clay.application.App3D
@param {DomQuery} dom Container dom element or a selector string that can be used in `querySelector`
@param {App3DNamespace} appNS Options and namespace used in creating app3D