diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
index bec2fa9b8d..56f6438e70 100644
--- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp
+++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
@@ -29,17 +29,16 @@
 
 #include "RenderableEntityItem.h"
 
-#include "RenderableBoxEntityItem.h"
 #include "RenderableLightEntityItem.h"
 #include "RenderableModelEntityItem.h"
 #include "RenderableParticleEffectEntityItem.h"
-#include "RenderableSphereEntityItem.h"
 #include "RenderableTextEntityItem.h"
 #include "RenderableWebEntityItem.h"
 #include "RenderableZoneEntityItem.h"
 #include "RenderableLineEntityItem.h"
 #include "RenderablePolyVoxEntityItem.h"
 #include "RenderablePolyLineEntityItem.h"
+#include "RenderableShapeEntityItem.h"
 #include "EntitiesRendererLogging.h"
 #include "AddressManager.h"
 #include <Rig.h>
@@ -56,8 +55,6 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
     _dontDoPrecisionPicking(false)
 {
     REGISTER_ENTITY_TYPE_WITH_FACTORY(Model, RenderableModelEntityItem::factory)
-    REGISTER_ENTITY_TYPE_WITH_FACTORY(Box, RenderableBoxEntityItem::factory)
-    REGISTER_ENTITY_TYPE_WITH_FACTORY(Sphere, RenderableSphereEntityItem::factory)
     REGISTER_ENTITY_TYPE_WITH_FACTORY(Light, RenderableLightEntityItem::factory)
     REGISTER_ENTITY_TYPE_WITH_FACTORY(Text, RenderableTextEntityItem::factory)
     REGISTER_ENTITY_TYPE_WITH_FACTORY(Web, RenderableWebEntityItem::factory)
@@ -66,7 +63,10 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
     REGISTER_ENTITY_TYPE_WITH_FACTORY(Line, RenderableLineEntityItem::factory)
     REGISTER_ENTITY_TYPE_WITH_FACTORY(PolyVox, RenderablePolyVoxEntityItem::factory)
     REGISTER_ENTITY_TYPE_WITH_FACTORY(PolyLine, RenderablePolyLineEntityItem::factory)
-    
+    REGISTER_ENTITY_TYPE_WITH_FACTORY(Shape, RenderableShapeEntityItem::factory)
+    REGISTER_ENTITY_TYPE_WITH_FACTORY(Box, RenderableShapeEntityItem::boxFactory)
+    REGISTER_ENTITY_TYPE_WITH_FACTORY(Sphere, RenderableShapeEntityItem::sphereFactory)
+
     _currentHoverOverEntityID = UNKNOWN_ENTITY_ID;
     _currentClickingOnEntityID = UNKNOWN_ENTITY_ID;
 }
diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp
deleted file mode 100644
index e392450c08..0000000000
--- a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-//
-//  RenderableBoxEntityItem.cpp
-//  libraries/entities-renderer/src/
-//
-//  Created by Brad Hefta-Gaub on 8/6/14.
-//  Copyright 2014 High Fidelity, Inc.
-//
-//  Distributed under the Apache License, Version 2.0.
-//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-#include "RenderableBoxEntityItem.h"
-
-#include <glm/gtx/quaternion.hpp>
-
-#include <gpu/Batch.h>
-
-#include <GeometryCache.h>
-#include <ObjectMotionState.h>
-#include <PerfStat.h>
-
-#include <render-utils/simple_vert.h>
-#include <render-utils/simple_frag.h>
-
-EntityItemPointer RenderableBoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
-    EntityItemPointer entity{ new RenderableBoxEntityItem(entityID) };
-    entity->setProperties(properties);
-    return entity;
-}
-
-void RenderableBoxEntityItem::setUserData(const QString& value) {
-    if (value != getUserData()) {
-        BoxEntityItem::setUserData(value);
-        if (_procedural) {
-            _procedural->parse(value);
-        }
-    }
-}
-
-void RenderableBoxEntityItem::render(RenderArgs* args) {
-    PerformanceTimer perfTimer("RenderableBoxEntityItem::render");
-    Q_ASSERT(getType() == EntityTypes::Box);
-    Q_ASSERT(args->_batch);
-
-    if (!_procedural) {
-        _procedural.reset(new Procedural(this->getUserData()));
-        _procedural->_vertexSource = simple_vert;
-        _procedural->_fragmentSource = simple_frag;
-        _procedural->_state->setCullMode(gpu::State::CULL_NONE);
-        _procedural->_state->setDepthTest(true, true, gpu::LESS_EQUAL);
-        _procedural->_state->setBlendFunction(false,
-            gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
-            gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
-    }
-
-    gpu::Batch& batch = *args->_batch;
-    glm::vec4 cubeColor(toGlm(getXColor()), getLocalRenderAlpha());
-
-    bool success;
-    auto transToCenter = getTransformToCenter(success);
-    if (!success) {
-        return;
-    }
-
-    batch.setModelTransform(transToCenter); // we want to include the scale as well
-    if (_procedural->ready()) {
-        _procedural->prepare(batch, getPosition(), getDimensions());
-        auto color = _procedural->getColor(cubeColor);
-        batch._glColor4f(color.r, color.g, color.b, color.a);
-        DependencyManager::get<GeometryCache>()->renderCube(batch);
-    } else {
-        DependencyManager::get<GeometryCache>()->renderSolidCubeInstance(batch, cubeColor);
-    }
-    static const auto triCount = DependencyManager::get<GeometryCache>()->getCubeTriangleCount();
-    args->_details._trianglesRendered += (int)triCount;
-}
diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.h b/libraries/entities-renderer/src/RenderableBoxEntityItem.h
deleted file mode 100644
index 67f881dbd8..0000000000
--- a/libraries/entities-renderer/src/RenderableBoxEntityItem.h
+++ /dev/null
@@ -1,34 +0,0 @@
-//
-//  RenderableBoxEntityItem.h
-//  libraries/entities-renderer/src/
-//
-//  Created by Brad Hefta-Gaub on 8/6/14.
-//  Copyright 2014 High Fidelity, Inc.
-//
-//  Distributed under the Apache License, Version 2.0.
-//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-#ifndef hifi_RenderableBoxEntityItem_h
-#define hifi_RenderableBoxEntityItem_h
-
-#include <BoxEntityItem.h>
-#include <procedural/Procedural.h>
-
-#include "RenderableEntityItem.h"
-
-class RenderableBoxEntityItem : public BoxEntityItem {
-public:
-    static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
-    RenderableBoxEntityItem(const EntityItemID& entityItemID) : BoxEntityItem(entityItemID) { }
-
-    virtual void render(RenderArgs* args) override;
-    virtual void setUserData(const QString& value) override;
-
-    SIMPLE_RENDERABLE()
-private:
-    QSharedPointer<Procedural> _procedural;
-};
-
-
-#endif // hifi_RenderableBoxEntityItem_h
diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
new file mode 100644
index 0000000000..7d30b7a47c
--- /dev/null
+++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
@@ -0,0 +1,113 @@
+//
+//  Created by Bradley Austin Davis on 2016/05/09
+//  Copyright 2013 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#include "RenderableShapeEntityItem.h"
+
+#include <glm/gtx/quaternion.hpp>
+
+#include <gpu/Batch.h>
+
+#include <DependencyManager.h>
+#include <GeometryCache.h>
+#include <PerfStat.h>
+
+#include <render-utils/simple_vert.h>
+#include <render-utils/simple_frag.h>
+
+// Sphere entities should fit inside a cube entity of the same size, so a sphere that has dimensions 1x1x1 
+// is a half unit sphere.  However, the geometry cache renders a UNIT sphere, so we need to scale down.
+static const float SPHERE_ENTITY_SCALE = 0.5f;
+
+static GeometryCache::Shape MAPPING[entity::NUM_SHAPES] = {
+    GeometryCache::Triangle,
+    GeometryCache::Quad,
+    GeometryCache::Circle,
+    GeometryCache::Cube,
+    GeometryCache::Sphere,
+    GeometryCache::Tetrahedron,
+    GeometryCache::Octahetron,
+    GeometryCache::Dodecahedron,
+    GeometryCache::Icosahedron,
+    GeometryCache::Torus,
+    GeometryCache::Cone,
+    GeometryCache::Cylinder,
+};
+
+
+RenderableShapeEntityItem::Pointer RenderableShapeEntityItem::baseFactory(const EntityItemID& entityID, const EntityItemProperties& properties) {
+    Pointer entity = std::make_shared<RenderableShapeEntityItem>(entityID);
+    entity->setProperties(properties);
+    return entity;
+}
+
+EntityItemPointer RenderableShapeEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
+    return baseFactory(entityID, properties);
+}
+
+EntityItemPointer RenderableShapeEntityItem::boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties) {
+    auto result = baseFactory(entityID, properties);
+    result->setShape(entity::Cube);
+    return result;
+}
+
+EntityItemPointer RenderableShapeEntityItem::sphereFactory(const EntityItemID& entityID, const EntityItemProperties& properties) {
+    auto result = baseFactory(entityID, properties);
+    result->setShape(entity::Sphere);
+    return result;
+}
+
+void RenderableShapeEntityItem::setUserData(const QString& value) {
+    if (value != getUserData()) {
+        ShapeEntityItem::setUserData(value);
+        if (_procedural) {
+            _procedural->parse(value);
+        }
+    }
+}
+
+void RenderableShapeEntityItem::render(RenderArgs* args) {
+    PerformanceTimer perfTimer("RenderableShapeEntityItem::render");
+    //Q_ASSERT(getType() == EntityTypes::Shape);
+    Q_ASSERT(args->_batch);
+
+    if (!_procedural) {
+        _procedural.reset(new Procedural(getUserData()));
+        _procedural->_vertexSource = simple_vert;
+        _procedural->_fragmentSource = simple_frag;
+        _procedural->_state->setCullMode(gpu::State::CULL_NONE);
+        _procedural->_state->setDepthTest(true, true, gpu::LESS_EQUAL);
+        _procedural->_state->setBlendFunction(false,
+            gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
+            gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
+    }
+
+    gpu::Batch& batch = *args->_batch;
+    glm::vec4 color(toGlm(getXColor()), getLocalRenderAlpha());
+    bool success;
+    Transform modelTransform = getTransformToCenter(success);
+    if (!success) {
+        return;
+    }
+    if (_shape != entity::Cube) {
+        modelTransform.postScale(SPHERE_ENTITY_SCALE);
+    }
+    batch.setModelTransform(modelTransform); // use a transform with scale, rotation, registration point and translation
+    if (_procedural->ready()) {
+        _procedural->prepare(batch, getPosition(), getDimensions());
+        auto outColor = _procedural->getColor(color);
+        batch._glColor4f(outColor.r, outColor.g, outColor.b, outColor.a);
+        DependencyManager::get<GeometryCache>()->renderShape(batch, MAPPING[_shape]);
+    } else {
+        // FIXME, support instanced multi-shape rendering using multidraw indirect
+        DependencyManager::get<GeometryCache>()->renderSolidShapeInstance(batch, MAPPING[_shape], color);
+    }
+
+
+    static const auto triCount = DependencyManager::get<GeometryCache>()->getShapeTriangleCount(MAPPING[_shape]);
+    args->_details._trianglesRendered += (int)triCount;
+}
diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h
new file mode 100644
index 0000000000..b18370b13c
--- /dev/null
+++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h
@@ -0,0 +1,36 @@
+//
+//  Created by Bradley Austin Davis on 2016/05/09
+//  Copyright 2013 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#ifndef hifi_RenderableShapeEntityItem_h
+#define hifi_RenderableShapeEntityItem_h
+
+#include <ShapeEntityItem.h>
+#include <procedural/Procedural.h>
+
+#include "RenderableEntityItem.h"
+
+class RenderableShapeEntityItem : public ShapeEntityItem {
+    using Pointer = std::shared_ptr<RenderableShapeEntityItem>;
+    static Pointer baseFactory(const EntityItemID& entityID, const EntityItemProperties& properties);
+public:
+    static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
+    static EntityItemPointer boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties);
+    static EntityItemPointer sphereFactory(const EntityItemID& entityID, const EntityItemProperties& properties);
+    RenderableShapeEntityItem(const EntityItemID& entityItemID) : ShapeEntityItem(entityItemID) {}
+
+    void render(RenderArgs* args) override;
+    void setUserData(const QString& value) override;
+
+    SIMPLE_RENDERABLE();
+
+private:
+    QSharedPointer<Procedural> _procedural;
+};
+
+
+#endif // hifi_RenderableShapeEntityItem_h
diff --git a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp
deleted file mode 100644
index c3437b0e4a..0000000000
--- a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-//
-//  RenderableSphereEntityItem.cpp
-//  interface/src
-//
-//  Created by Brad Hefta-Gaub on 8/6/14.
-//  Copyright 2014 High Fidelity, Inc.
-//
-//  Distributed under the Apache License, Version 2.0.
-//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-#include "RenderableSphereEntityItem.h"
-
-#include <glm/gtx/quaternion.hpp>
-
-#include <gpu/Batch.h>
-
-#include <DependencyManager.h>
-#include <GeometryCache.h>
-#include <PerfStat.h>
-
-#include <render-utils/simple_vert.h>
-#include <render-utils/simple_frag.h>
-
-// Sphere entities should fit inside a cube entity of the same size, so a sphere that has dimensions 1x1x1 
-// is a half unit sphere.  However, the geometry cache renders a UNIT sphere, so we need to scale down.
-static const float SPHERE_ENTITY_SCALE = 0.5f;
-
-
-EntityItemPointer RenderableSphereEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
-    EntityItemPointer entity{ new RenderableSphereEntityItem(entityID) };
-    entity->setProperties(properties);
-    return entity;
-}
-
-void RenderableSphereEntityItem::setUserData(const QString& value) {
-    if (value != getUserData()) {
-        SphereEntityItem::setUserData(value);
-        if (_procedural) {
-            _procedural->parse(value);
-        }
-    }
-}
-
-void RenderableSphereEntityItem::render(RenderArgs* args) {
-    PerformanceTimer perfTimer("RenderableSphereEntityItem::render");
-    Q_ASSERT(getType() == EntityTypes::Sphere);
-    Q_ASSERT(args->_batch);
-
-    if (!_procedural) {
-        _procedural.reset(new Procedural(getUserData()));
-        _procedural->_vertexSource = simple_vert;
-        _procedural->_fragmentSource = simple_frag;
-        _procedural->_state->setCullMode(gpu::State::CULL_NONE);
-        _procedural->_state->setDepthTest(true, true, gpu::LESS_EQUAL);
-        _procedural->_state->setBlendFunction(false,
-            gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
-            gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
-    }
-
-    gpu::Batch& batch = *args->_batch;
-    glm::vec4 sphereColor(toGlm(getXColor()), getLocalRenderAlpha());
-    bool success;
-    Transform modelTransform = getTransformToCenter(success);
-    if (!success) {
-        return;
-    }
-    modelTransform.postScale(SPHERE_ENTITY_SCALE);
-    batch.setModelTransform(modelTransform); // use a transform with scale, rotation, registration point and translation
-    if (_procedural->ready()) {
-        _procedural->prepare(batch, getPosition(), getDimensions());
-        auto color = _procedural->getColor(sphereColor);
-        batch._glColor4f(color.r, color.g, color.b, color.a);
-        DependencyManager::get<GeometryCache>()->renderSphere(batch);
-    } else {
-        DependencyManager::get<GeometryCache>()->renderSolidSphereInstance(batch, sphereColor);
-    }
-    static const auto triCount = DependencyManager::get<GeometryCache>()->getSphereTriangleCount();
-    args->_details._trianglesRendered += (int)triCount;
-}
diff --git a/libraries/entities-renderer/src/RenderableSphereEntityItem.h b/libraries/entities-renderer/src/RenderableSphereEntityItem.h
deleted file mode 100644
index 5efe49854a..0000000000
--- a/libraries/entities-renderer/src/RenderableSphereEntityItem.h
+++ /dev/null
@@ -1,35 +0,0 @@
-//
-//  RenderableSphereEntityItem.h
-//  interface/src/entities
-//
-//  Created by Brad Hefta-Gaub on 8/6/14.
-//  Copyright 2014 High Fidelity, Inc.
-//
-//  Distributed under the Apache License, Version 2.0.
-//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-#ifndef hifi_RenderableSphereEntityItem_h
-#define hifi_RenderableSphereEntityItem_h
-
-#include <SphereEntityItem.h>
-#include <procedural/Procedural.h>
-
-#include "RenderableEntityItem.h"
-
-class RenderableSphereEntityItem : public SphereEntityItem {
-public:
-    static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
-    RenderableSphereEntityItem(const EntityItemID& entityItemID) : SphereEntityItem(entityItemID) { }
-
-    virtual void render(RenderArgs* args) override;
-    virtual void setUserData(const QString& value) override;
-
-    SIMPLE_RENDERABLE();
-
-private:
-    QSharedPointer<Procedural> _procedural;
-};
-
-
-#endif // hifi_RenderableSphereEntityItem_h
diff --git a/libraries/entities/src/BoxEntityItem.cpp b/libraries/entities/src/BoxEntityItem.cpp
deleted file mode 100644
index bf02d383ab..0000000000
--- a/libraries/entities/src/BoxEntityItem.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-//
-//  BoxEntityItem.cpp
-//  libraries/entities/src
-//
-//  Created by Brad Hefta-Gaub on 12/4/13.
-//  Copyright 2013 High Fidelity, Inc.
-//
-//  Distributed under the Apache License, Version 2.0.
-//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-
-#include <QDebug>
-
-#include <ByteCountCoding.h>
-
-#include "BoxEntityItem.h"
-#include "EntitiesLogging.h"
-#include "EntityItemProperties.h"
-#include "EntityTree.h"
-#include "EntityTreeElement.h"
-
-EntityItemPointer BoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
-    EntityItemPointer entity { new BoxEntityItem(entityID) };
-    entity->setProperties(properties);
-    return entity;
-}
-
-BoxEntityItem::BoxEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) {
-    _type = EntityTypes::Box;
-}
-
-EntityItemProperties BoxEntityItem::getProperties(EntityPropertyFlags desiredProperties) const {
-    EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class
-
-    properties._color = getXColor();
-    properties._colorChanged = false;
-
-    return properties;
-}
-
-bool BoxEntityItem::setProperties(const EntityItemProperties& properties) {
-    bool somethingChanged = false;
-    somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
-
-    SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
-
-    if (somethingChanged) {
-        bool wantDebug = false;
-        if (wantDebug) {
-            uint64_t now = usecTimestampNow();
-            int elapsed = now - getLastEdited();
-            qCDebug(entities) << "BoxEntityItem::setProperties() AFTER update... edited AGO=" << elapsed <<
-                    "now=" << now << " getLastEdited()=" << getLastEdited();
-        }
-        setLastEdited(properties._lastEdited);
-    }
-    return somethingChanged;
-}
-
-int BoxEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, 
-                                                ReadBitstreamToTreeParams& args,
-                                                EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
-                                                bool& somethingChanged) {
-
-    int bytesRead = 0;
-    const unsigned char* dataAt = data;
-
-    READ_ENTITY_PROPERTY(PROP_COLOR, rgbColor, setColor);
-
-    return bytesRead;
-}
-
-
-// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
-EntityPropertyFlags BoxEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
-    EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
-    requestedProperties += PROP_COLOR;
-    return requestedProperties;
-}
-
-void BoxEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, 
-                                    EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
-                                    EntityPropertyFlags& requestedProperties,
-                                    EntityPropertyFlags& propertyFlags,
-                                    EntityPropertyFlags& propertiesDidntFit,
-                                    int& propertyCount, 
-                                    OctreeElement::AppendState& appendState) const { 
-
-    bool successPropertyFits = true;
-
-    APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor());
-}
-
-void BoxEntityItem::debugDump() const {
-    quint64 now = usecTimestampNow();
-    qCDebug(entities) << "   BOX EntityItem id:" << getEntityItemID() << "---------------------------------------------";
-    qCDebug(entities) << "               color:" << _color[0] << "," << _color[1] << "," << _color[2];
-    qCDebug(entities) << "            position:" << debugTreeVector(getPosition());
-    qCDebug(entities) << "          dimensions:" << debugTreeVector(getDimensions());
-    qCDebug(entities) << "       getLastEdited:" << debugTime(getLastEdited(), now);
-}
-
diff --git a/libraries/entities/src/BoxEntityItem.h b/libraries/entities/src/BoxEntityItem.h
deleted file mode 100644
index 6196346b9a..0000000000
--- a/libraries/entities/src/BoxEntityItem.h
+++ /dev/null
@@ -1,64 +0,0 @@
-//
-//  BoxEntityItem.h
-//  libraries/entities/src
-//
-//  Created by Brad Hefta-Gaub on 12/4/13.
-//  Copyright 2013 High Fidelity, Inc.
-//
-//  Distributed under the Apache License, Version 2.0.
-//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-#ifndef hifi_BoxEntityItem_h
-#define hifi_BoxEntityItem_h
-
-#include "EntityItem.h" 
-
-class BoxEntityItem : public EntityItem {
-public:
-    static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
-
-    BoxEntityItem(const EntityItemID& entityItemID);
-    
-    ALLOW_INSTANTIATION // This class can be instantiated
-    
-    // methods for getting/setting all properties of an entity
-    virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const;
-    virtual bool setProperties(const EntityItemProperties& properties);
-
-    // TODO: eventually only include properties changed since the params.lastViewFrustumSent time
-    virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const;
-
-    virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, 
-                                    EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
-                                    EntityPropertyFlags& requestedProperties,
-                                    EntityPropertyFlags& propertyFlags,
-                                    EntityPropertyFlags& propertiesDidntFit,
-                                    int& propertyCount, 
-                                    OctreeElement::AppendState& appendState) const;
-
-    virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, 
-                                                ReadBitstreamToTreeParams& args,
-                                                EntityPropertyFlags& propertyFlags, bool overwriteLocalData, 
-                                                bool& somethingChanged);
-
-    const rgbColor& getColor() const { return _color; }
-    xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; }
-
-    void setColor(const rgbColor& value) { memcpy(_color, value, sizeof(_color)); }
-    void setColor(const xColor& value) {
-        _color[RED_INDEX] = value.red;
-        _color[GREEN_INDEX] = value.green;
-        _color[BLUE_INDEX] = value.blue;
-    }
-    
-    virtual ShapeType getShapeType() const { return SHAPE_TYPE_BOX; }
-    virtual bool shouldBePhysical() const { return !isDead(); }
-
-    virtual void debugDump() const;
-
-protected:
-    rgbColor _color;
-};
-
-#endif // hifi_BoxEntityItem_h
diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp
index 99285b4986..f273507d0d 100644
--- a/libraries/entities/src/EntityItemProperties.cpp
+++ b/libraries/entities/src/EntityItemProperties.cpp
@@ -689,6 +689,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
     COPY_PROPERTY_FROM_QSCRIPTVALUE(jointRotations, qVectorQuat, setJointRotations);
     COPY_PROPERTY_FROM_QSCRIPTVALUE(jointTranslationsSet, qVectorBool, setJointTranslationsSet);
     COPY_PROPERTY_FROM_QSCRIPTVALUE(jointTranslations, qVectorVec3, setJointTranslations);
+    COPY_PROPERTY_FROM_QSCRIPTVALUE(shape, QString, setShape);
 
     COPY_PROPERTY_FROM_QSCRIPTVALUE(flyingAllowed, bool, setFlyingAllowed);
     COPY_PROPERTY_FROM_QSCRIPTVALUE(ghostingAllowed, bool, setGhostingAllowed);
@@ -846,6 +847,8 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
         ADD_PROPERTY_TO_MAP(PROP_JOINT_TRANSLATIONS_SET, JointTranslationsSet, jointTranslationsSet, QVector<bool>);
         ADD_PROPERTY_TO_MAP(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector<glm::vec3>);
 
+        ADD_PROPERTY_TO_MAP(PROP_SHAPE, Shape, shape, QString);
+
         ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_URL, Animation, animation, URL, url);
         ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_FPS, Animation, animation, FPS, fps);
         ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_FRAME_INDEX, Animation, animation, CurrentFrame, currentFrame);
@@ -1141,7 +1144,9 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
                 APPEND_ENTITY_PROPERTY(PROP_STROKE_WIDTHS, properties.getStrokeWidths());
                 APPEND_ENTITY_PROPERTY(PROP_TEXTURES, properties.getTextures());
             }
-
+            if (properties.getType() == EntityTypes::Shape) {
+                APPEND_ENTITY_PROPERTY(PROP_SHAPE, properties.getShape());
+            }
             APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, properties.getMarketplaceID());
             APPEND_ENTITY_PROPERTY(PROP_NAME, properties.getName());
             APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, properties.getCollisionSoundURL());
@@ -1429,6 +1434,9 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
         READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STROKE_WIDTHS, QVector<float>, setStrokeWidths);
         READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXTURES, QString, setTextures);
     }
+    if (properties.getType() == EntityTypes::Shape) {
+        READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE, QString, setShape);
+    }
 
     READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MARKETPLACE_ID, QString, setMarketplaceID);
     READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NAME, QString, setName);
@@ -1915,6 +1923,7 @@ QList<QString> EntityItemProperties::listChangedProperties() {
     if (queryAACubeChanged()) {
         out += "queryAACube";
     }
+
     if (clientOnlyChanged()) {
         out += "clientOnly";
     }
@@ -1929,6 +1938,10 @@ QList<QString> EntityItemProperties::listChangedProperties() {
         out += "ghostingAllowed";
     }
 
+    if (shapeChanged()) {
+        out += "shape";
+    }
+
     getAnimation().listChangedProperties(out);
     getKeyLight().listChangedProperties(out);
     getSkybox().listChangedProperties(out);
diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h
index 59749cfef5..6018ba793f 100644
--- a/libraries/entities/src/EntityItemProperties.h
+++ b/libraries/entities/src/EntityItemProperties.h
@@ -50,7 +50,7 @@ const quint64 UNKNOWN_CREATED_TIME = 0;
 /// A collection of properties of an entity item used in the scripting API. Translates between the actual properties of an
 /// entity and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete
 /// set of entity item properties via JavaScript hashes/QScriptValues
-/// all units for position, dimensions, etc are in meter units
+/// all units for SI units (meter, second, radian, etc) 
 class EntityItemProperties {
     friend class EntityItem; // TODO: consider removing this friend relationship and use public methods
     friend class ModelEntityItem; // TODO: consider removing this friend relationship and use public methods
@@ -64,6 +64,7 @@ class EntityItemProperties {
     friend class LineEntityItem; // TODO: consider removing this friend relationship and use public methods
     friend class PolyVoxEntityItem; // TODO: consider removing this friend relationship and use public methods
     friend class PolyLineEntityItem; // TODO: consider removing this friend relationship and use public methods
+    friend class ShapeEntityItem; // TODO: consider removing this friend relationship and use public methods
 public:
     EntityItemProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags());
     virtual ~EntityItemProperties() = default;
@@ -195,6 +196,7 @@ public:
     DEFINE_PROPERTY_REF(PROP_PARENT_ID, ParentID, parentID, QUuid, UNKNOWN_ENTITY_ID);
     DEFINE_PROPERTY_REF(PROP_PARENT_JOINT_INDEX, ParentJointIndex, parentJointIndex, quint16, -1);
     DEFINE_PROPERTY_REF(PROP_QUERY_AA_CUBE, QueryAACube, queryAACube, AACube, AACube());
+    DEFINE_PROPERTY_REF(PROP_SHAPE, Shape, shape, QString, "Sphere");
 
     // these are used when bouncing location data into and out of scripts
     DEFINE_PROPERTY_REF(PROP_LOCAL_POSITION, LocalPosition, localPosition, glmVec3, ENTITY_ITEM_ZERO_VEC3);
diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h
index 20c451eaae..36bb37c8f3 100644
--- a/libraries/entities/src/EntityPropertyFlags.h
+++ b/libraries/entities/src/EntityPropertyFlags.h
@@ -175,6 +175,8 @@ enum EntityPropertyList {
     PROP_CLIENT_ONLY, // doesn't go over wire
     PROP_OWNING_AVATAR_ID, // doesn't go over wire
 
+    PROP_SHAPE,
+
     ////////////////////////////////////////////////////////////////////////////////////////////////////
     // ATTENTION: add new properties to end of list just ABOVE this line
     PROP_AFTER_LAST_ITEM,
diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp
index 52c2242629..7b1133c2aa 100644
--- a/libraries/entities/src/EntityTypes.cpp
+++ b/libraries/entities/src/EntityTypes.cpp
@@ -18,17 +18,16 @@
 #include "EntityItemProperties.h"
 #include "EntityTypes.h"
 
-#include "BoxEntityItem.h"
 #include "LightEntityItem.h"
 #include "ModelEntityItem.h"
 #include "ParticleEffectEntityItem.h"
-#include "SphereEntityItem.h"
 #include "TextEntityItem.h"
 #include "WebEntityItem.h"
 #include "ZoneEntityItem.h"
 #include "LineEntityItem.h"
 #include "PolyVoxEntityItem.h"
 #include "PolyLineEntityItem.h"
+#include "ShapeEntityItem.h"
 
 QMap<EntityTypes::EntityType, QString> EntityTypes::_typeToNameMap;
 QMap<QString, EntityTypes::EntityType> EntityTypes::_nameToTypeMap;
@@ -39,16 +38,17 @@ const QString ENTITY_TYPE_NAME_UNKNOWN = "Unknown";
 
 // Register Entity the default implementations of entity types here...
 REGISTER_ENTITY_TYPE(Model)
-REGISTER_ENTITY_TYPE(Box)
 REGISTER_ENTITY_TYPE(Web)
-REGISTER_ENTITY_TYPE(Sphere)
 REGISTER_ENTITY_TYPE(Light)
 REGISTER_ENTITY_TYPE(Text)
 REGISTER_ENTITY_TYPE(ParticleEffect)
 REGISTER_ENTITY_TYPE(Zone)
 REGISTER_ENTITY_TYPE(Line)
 REGISTER_ENTITY_TYPE(PolyVox)
-REGISTER_ENTITY_TYPE(PolyLine);
+REGISTER_ENTITY_TYPE(PolyLine)
+REGISTER_ENTITY_TYPE(Shape)
+REGISTER_ENTITY_TYPE_WITH_FACTORY(Box, ShapeEntityItem::boxFactory)
+REGISTER_ENTITY_TYPE_WITH_FACTORY(Sphere, ShapeEntityItem::sphereFactory)
 
 const QString& EntityTypes::getEntityTypeName(EntityType entityType) {
     QMap<EntityType, QString>::iterator matchedTypeName = _typeToNameMap.find(entityType);
diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h
index 3536327d18..be3be03713 100644
--- a/libraries/entities/src/EntityTypes.h
+++ b/libraries/entities/src/EntityTypes.h
@@ -48,7 +48,8 @@ public:
         Line,
         PolyVox,
         PolyLine,
-        LAST = PolyLine
+        Shape,
+        LAST = Shape
     } EntityType;
 
     static const QString& getEntityTypeName(EntityType entityType);
@@ -72,6 +73,15 @@ private:
 #define REGISTER_ENTITY_TYPE(x) static bool x##Registration = \
             EntityTypes::registerEntityType(EntityTypes::x, #x, x##EntityItem::factory);
 
+
+struct EntityRegistrationChecker {
+    EntityRegistrationChecker(bool result, const char* debugMessage) {
+        if (!result) {
+            qDebug() << debugMessage;
+        }
+    }
+};
+
 /// Macro for registering entity types with an overloaded factory. Like using the REGISTER_ENTITY_TYPE macro: Make sure to add
 /// an element to the EntityType enum with your name. But unlike  REGISTER_ENTITY_TYPE, your class can be named anything
 /// so long as you provide a static method passed to the macro, that takes an EnityItemID, and EntityItemProperties and 
@@ -79,9 +89,9 @@ private:
 //        static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
 #define REGISTER_ENTITY_TYPE_WITH_FACTORY(x,y) static bool x##Registration = \
             EntityTypes::registerEntityType(EntityTypes::x, #x, y); \
-            if (!x##Registration) { \
-                qDebug() << "UNEXPECTED: REGISTER_ENTITY_TYPE_WITH_FACTORY(" #x "," #y ") FAILED.!"; \
-            }
+            EntityRegistrationChecker x##RegistrationChecker( \
+                x##Registration, \
+                "UNEXPECTED: REGISTER_ENTITY_TYPE_WITH_FACTORY(" #x "," #y ") FAILED.!");
 
 
 #endif // hifi_EntityTypes_h
diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp
new file mode 100644
index 0000000000..a24c7e1df5
--- /dev/null
+++ b/libraries/entities/src/ShapeEntityItem.cpp
@@ -0,0 +1,230 @@
+//
+//  Created by Bradley Austin Davis on 2016/05/09
+//  Copyright 2013 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+
+#include <glm/gtx/transform.hpp>
+
+#include <QtCore/QDebug>
+
+#include <GeometryUtil.h>
+
+#include "EntitiesLogging.h"
+#include "EntityItemProperties.h"
+#include "EntityTree.h"
+#include "EntityTreeElement.h"
+#include "ShapeEntityItem.h"
+
+namespace entity {
+    static const std::vector<QString> shapeStrings { { 
+        "Triangle", 
+        "Quad", 
+        "Circle", 
+        "Cube", 
+        "Sphere", 
+        "Tetrahedron", 
+        "Octahetron", 
+        "Dodecahedron", 
+        "Icosahedron", 
+        "Torus", 
+        "Cone", 
+        "Cylinder" 
+    } };
+
+    Shape shapeFromString(const ::QString& shapeString) {
+        for (size_t i = 0; i < shapeStrings.size(); ++i) {
+            if (shapeString.toLower() == shapeStrings[i].toLower()) {
+                return static_cast<Shape>(i);
+            }
+        }
+        return Shape::Sphere;
+    }
+
+    ::QString stringFromShape(Shape shape) {
+        return shapeStrings[shape];
+    }
+}
+
+ShapeEntityItem::Pointer ShapeEntityItem::baseFactory(const EntityItemID& entityID, const EntityItemProperties& properties) {
+    Pointer entity { new ShapeEntityItem(entityID) };
+    entity->setProperties(properties);
+    return entity;
+}
+
+EntityItemPointer ShapeEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
+    return baseFactory(entityID, properties);
+}
+
+EntityItemPointer ShapeEntityItem::boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties) {
+    auto result = baseFactory(entityID, properties);
+    result->setShape(entity::Shape::Cube);
+    return result;
+}
+
+EntityItemPointer ShapeEntityItem::sphereFactory(const EntityItemID& entityID, const EntityItemProperties& properties) {
+    auto result = baseFactory(entityID, properties);
+    result->setShape(entity::Shape::Sphere);
+    return result;
+}
+
+// our non-pure virtual subclass for now...
+ShapeEntityItem::ShapeEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) {
+    _type = EntityTypes::Shape;
+    _volumeMultiplier *= PI / 6.0f;
+}
+
+EntityItemProperties ShapeEntityItem::getProperties(EntityPropertyFlags desiredProperties) const {
+    EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class
+    properties.setColor(getXColor());
+    properties.setShape(entity::stringFromShape(getShape()));
+    return properties;
+}
+
+void ShapeEntityItem::setShape(const entity::Shape& shape) {
+    _shape = shape;
+    switch (_shape) {
+        case entity::Shape::Cube:
+            _type = EntityTypes::Box;
+            break;
+        case entity::Shape::Sphere:
+            _type = EntityTypes::Sphere;
+            break;
+        default:
+            _type = EntityTypes::Shape;
+            break;
+    }
+}
+
+bool ShapeEntityItem::setProperties(const EntityItemProperties& properties) {
+    bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
+
+    SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha);
+    SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
+    SET_ENTITY_PROPERTY_FROM_PROPERTIES(shape, setShape);
+
+    if (somethingChanged) {
+        bool wantDebug = false;
+        if (wantDebug) {
+            uint64_t now = usecTimestampNow();
+            int elapsed = now - getLastEdited();
+            qCDebug(entities) << "ShapeEntityItem::setProperties() AFTER update... edited AGO=" << elapsed <<
+                    "now=" << now << " getLastEdited()=" << getLastEdited();
+        }
+        setLastEdited(properties.getLastEdited());
+    }
+    return somethingChanged;
+}
+
+int ShapeEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
+                                                ReadBitstreamToTreeParams& args,
+                                                EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
+                                                bool& somethingChanged) {
+
+    int bytesRead = 0;
+    const unsigned char* dataAt = data;
+
+    READ_ENTITY_PROPERTY(PROP_SHAPE, QString, setShape);
+    READ_ENTITY_PROPERTY(PROP_COLOR, rgbColor, setColor);
+    READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha);
+
+    return bytesRead;
+}
+
+
+// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
+EntityPropertyFlags ShapeEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
+    EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
+    requestedProperties += PROP_SHAPE;
+    requestedProperties += PROP_COLOR;
+    requestedProperties += PROP_ALPHA;
+    return requestedProperties;
+}
+
+void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
+                                    EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
+                                    EntityPropertyFlags& requestedProperties,
+                                    EntityPropertyFlags& propertyFlags,
+                                    EntityPropertyFlags& propertiesDidntFit,
+                                    int& propertyCount, 
+                                    OctreeElement::AppendState& appendState) const { 
+
+    bool successPropertyFits = true;
+    APPEND_ENTITY_PROPERTY(PROP_SHAPE, entity::stringFromShape(getShape()));
+    APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor());
+    APPEND_ENTITY_PROPERTY(PROP_COLOR, getAlpha());
+}
+
+// This value specifes how the shape should be treated by physics calculations.  
+// For now, all polys will act as spheres
+ShapeType ShapeEntityItem::getShapeType() const {
+    return SHAPE_TYPE_ELLIPSOID;
+}
+
+void ShapeEntityItem::setColor(const rgbColor& value) {
+    memcpy(_color, value, sizeof(rgbColor));
+}
+
+xColor ShapeEntityItem::getXColor() const {
+    return xColor { _color[0], _color[1], _color[2] };
+}
+
+void ShapeEntityItem::setColor(const xColor& value) {
+    setColor(rgbColor { value.red, value.green, value.blue });
+}
+
+QColor ShapeEntityItem::getQColor() const {
+    auto& color = getColor();
+    return QColor(color[0], color[1], color[2], (int)(getAlpha() * 255));
+}
+
+void ShapeEntityItem::setColor(const QColor& value) {
+    setColor(rgbColor { (uint8_t)value.red(), (uint8_t)value.green(), (uint8_t)value.blue() });
+    setAlpha(value.alpha());
+}
+
+bool ShapeEntityItem::supportsDetailedRayIntersection() const {
+    return _shape == entity::Sphere;
+}
+
+bool ShapeEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
+                                                   bool& keepSearching, OctreeElementPointer& element,
+                                                   float& distance, BoxFace& face, glm::vec3& surfaceNormal,
+                                                   void** intersectedObject, bool precisionPicking) const {
+    // determine the ray in the frame of the entity transformed from a unit sphere
+    glm::mat4 entityToWorldMatrix = getEntityToWorldMatrix();
+    glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
+    glm::vec3 entityFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f));
+    glm::vec3 entityFrameDirection = glm::normalize(glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f)));
+
+    float localDistance;
+    // NOTE: unit sphere has center of 0,0,0 and radius of 0.5
+    if (findRaySphereIntersection(entityFrameOrigin, entityFrameDirection, glm::vec3(0.0f), 0.5f, localDistance)) {
+        // determine where on the unit sphere the hit point occured
+        glm::vec3 entityFrameHitAt = entityFrameOrigin + (entityFrameDirection * localDistance);
+        // then translate back to work coordinates
+        glm::vec3 hitAt = glm::vec3(entityToWorldMatrix * glm::vec4(entityFrameHitAt, 1.0f));
+        distance = glm::distance(origin, hitAt);
+        bool success;
+        surfaceNormal = glm::normalize(hitAt - getCenterPosition(success));
+        if (!success) {
+            return false;
+        }
+        return true;
+    }
+    return false;
+}
+
+void ShapeEntityItem::debugDump() const {
+    quint64 now = usecTimestampNow();
+    qCDebug(entities) << "SHAPE EntityItem id:" << getEntityItemID() << "---------------------------------------------";
+    qCDebug(entities) << "              shape:" << stringFromShape(_shape);
+    qCDebug(entities) << "              color:" << _color[0] << "," << _color[1] << "," << _color[2];
+    qCDebug(entities) << "           position:" << debugTreeVector(getPosition());
+    qCDebug(entities) << "         dimensions:" << debugTreeVector(getDimensions());
+    qCDebug(entities) << "      getLastEdited:" << debugTime(getLastEdited(), now);
+}
+
diff --git a/libraries/entities/src/ShapeEntityItem.h b/libraries/entities/src/ShapeEntityItem.h
new file mode 100644
index 0000000000..f3cb2abd66
--- /dev/null
+++ b/libraries/entities/src/ShapeEntityItem.h
@@ -0,0 +1,103 @@
+//
+//  Created by Bradley Austin Davis on 2016/05/09
+//  Copyright 2013 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#ifndef hifi_ShapeEntityItem_h
+#define hifi_ShapeEntityItem_h
+
+#include "EntityItem.h"
+
+namespace entity {
+    enum Shape {
+        Triangle,
+        Quad,
+        Circle,
+        Cube,
+        Sphere,
+        Tetrahedron,
+        Octahetron,
+        Dodecahedron,
+        Icosahedron,
+        Torus,
+        Cone,
+        Cylinder,
+        NUM_SHAPES,
+    };
+
+    Shape shapeFromString(const ::QString& shapeString);
+    ::QString stringFromShape(Shape shape);
+}
+
+
+class ShapeEntityItem : public EntityItem {
+    using Pointer = std::shared_ptr<ShapeEntityItem>;
+    static Pointer baseFactory(const EntityItemID& entityID, const EntityItemProperties& properties);
+public:
+    static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
+    static EntityItemPointer sphereFactory(const EntityItemID& entityID, const EntityItemProperties& properties);
+    static EntityItemPointer boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties);
+
+    ShapeEntityItem(const EntityItemID& entityItemID);
+
+    void pureVirtualFunctionPlaceHolder() override { };
+    // Triggers warnings on OSX
+    //ALLOW_INSTANTIATION 
+    
+    // methods for getting/setting all properties of an entity
+    EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
+    bool setProperties(const EntityItemProperties& properties) override;
+
+    EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
+
+    void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
+                                    EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
+                                    EntityPropertyFlags& requestedProperties,
+                                    EntityPropertyFlags& propertyFlags,
+                                    EntityPropertyFlags& propertiesDidntFit,
+                                    int& propertyCount, 
+                                    OctreeElement::AppendState& appendState) const override;
+
+    int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
+                                                ReadBitstreamToTreeParams& args,
+                                                EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
+                                                bool& somethingChanged) override;
+
+    entity::Shape getShape() const { return _shape; }
+    void setShape(const entity::Shape& shape);
+    void setShape(const QString& shape) { setShape(entity::shapeFromString(shape)); }
+
+    float getAlpha() const { return _alpha; };
+    void setAlpha(float alpha) { _alpha = alpha; }
+
+    const rgbColor& getColor() const { return _color; }
+    void setColor(const rgbColor& value);
+
+    xColor getXColor() const;
+    void setColor(const xColor& value);
+
+    QColor getQColor() const;
+    void setColor(const QColor& value);
+
+    ShapeType getShapeType() const override;
+    bool shouldBePhysical() const override { return !isDead(); }
+    
+    bool supportsDetailedRayIntersection() const override;
+    bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
+                                                bool& keepSearching, OctreeElementPointer& element, float& distance,
+                                                BoxFace& face, glm::vec3& surfaceNormal,
+                                                void** intersectedObject, bool precisionPicking) const override;
+
+    void debugDump() const override;
+
+protected:
+
+    float _alpha { 1 };
+    rgbColor _color;
+    entity::Shape _shape { entity::Shape::Sphere };
+};
+
+#endif // hifi_ShapeEntityItem_h
diff --git a/libraries/entities/src/SphereEntityItem.cpp b/libraries/entities/src/SphereEntityItem.cpp
deleted file mode 100644
index 7ad7b39f20..0000000000
--- a/libraries/entities/src/SphereEntityItem.cpp
+++ /dev/null
@@ -1,132 +0,0 @@
-//
-//  SphereEntityItem.cpp
-//  libraries/entities/src
-//
-//  Created by Brad Hefta-Gaub on 12/4/13.
-//  Copyright 2013 High Fidelity, Inc.
-//
-//  Distributed under the Apache License, Version 2.0.
-//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-
-#include <glm/gtx/transform.hpp>
-
-#include <QDebug>
-
-#include <ByteCountCoding.h>
-#include <GeometryUtil.h>
-
-#include "EntitiesLogging.h"
-#include "EntityItemProperties.h"
-#include "EntityTree.h"
-#include "EntityTreeElement.h"
-#include "SphereEntityItem.h"
-
-EntityItemPointer SphereEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
-    EntityItemPointer entity { new SphereEntityItem(entityID) };
-    entity->setProperties(properties);
-    return entity;
-}
-
-// our non-pure virtual subclass for now...
-SphereEntityItem::SphereEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) {
-    _type = EntityTypes::Sphere;
-    _volumeMultiplier *= PI / 6.0f;
-}
-
-EntityItemProperties SphereEntityItem::getProperties(EntityPropertyFlags desiredProperties) const {
-    EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class
-    properties.setColor(getXColor());
-    return properties;
-}
-
-bool SphereEntityItem::setProperties(const EntityItemProperties& properties) {
-    bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
-
-    SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
-
-    if (somethingChanged) {
-        bool wantDebug = false;
-        if (wantDebug) {
-            uint64_t now = usecTimestampNow();
-            int elapsed = now - getLastEdited();
-            qCDebug(entities) << "SphereEntityItem::setProperties() AFTER update... edited AGO=" << elapsed <<
-                    "now=" << now << " getLastEdited()=" << getLastEdited();
-        }
-        setLastEdited(properties.getLastEdited());
-    }
-    return somethingChanged;
-}
-
-int SphereEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, 
-                                                ReadBitstreamToTreeParams& args,
-                                                EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
-                                                bool& somethingChanged) {
-
-    int bytesRead = 0;
-    const unsigned char* dataAt = data;
-
-    READ_ENTITY_PROPERTY(PROP_COLOR, rgbColor, setColor);
-
-    return bytesRead;
-}
-
-
-// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
-EntityPropertyFlags SphereEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
-    EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
-    requestedProperties += PROP_COLOR;
-    return requestedProperties;
-}
-
-void SphereEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, 
-                                    EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
-                                    EntityPropertyFlags& requestedProperties,
-                                    EntityPropertyFlags& propertyFlags,
-                                    EntityPropertyFlags& propertiesDidntFit,
-                                    int& propertyCount, 
-                                    OctreeElement::AppendState& appendState) const { 
-
-    bool successPropertyFits = true;
-    APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor());
-}
-
-bool SphereEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
-                                                   bool& keepSearching, OctreeElementPointer& element,
-                                                   float& distance, BoxFace& face, glm::vec3& surfaceNormal,
-                                                   void** intersectedObject, bool precisionPicking) const {
-    // determine the ray in the frame of the entity transformed from a unit sphere
-    glm::mat4 entityToWorldMatrix = getEntityToWorldMatrix();
-    glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
-    glm::vec3 entityFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f));
-    glm::vec3 entityFrameDirection = glm::normalize(glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f)));
-
-    float localDistance;
-    // NOTE: unit sphere has center of 0,0,0 and radius of 0.5
-    if (findRaySphereIntersection(entityFrameOrigin, entityFrameDirection, glm::vec3(0.0f), 0.5f, localDistance)) {
-        // determine where on the unit sphere the hit point occured
-        glm::vec3 entityFrameHitAt = entityFrameOrigin + (entityFrameDirection * localDistance);
-        // then translate back to work coordinates
-        glm::vec3 hitAt = glm::vec3(entityToWorldMatrix * glm::vec4(entityFrameHitAt, 1.0f));
-        distance = glm::distance(origin, hitAt);
-        bool success;
-        surfaceNormal = glm::normalize(hitAt - getCenterPosition(success));
-        if (!success) {
-            return false;
-        }
-        return true;
-    }
-    return false;
-}
-
-
-void SphereEntityItem::debugDump() const {
-    quint64 now = usecTimestampNow();
-    qCDebug(entities) << "SHPERE EntityItem id:" << getEntityItemID() << "---------------------------------------------";
-    qCDebug(entities) << "               color:" << _color[0] << "," << _color[1] << "," << _color[2];
-    qCDebug(entities) << "            position:" << debugTreeVector(getPosition());
-    qCDebug(entities) << "          dimensions:" << debugTreeVector(getDimensions());
-    qCDebug(entities) << "       getLastEdited:" << debugTime(getLastEdited(), now);
-}
-
diff --git a/libraries/entities/src/SphereEntityItem.h b/libraries/entities/src/SphereEntityItem.h
deleted file mode 100644
index fda5eab009..0000000000
--- a/libraries/entities/src/SphereEntityItem.h
+++ /dev/null
@@ -1,70 +0,0 @@
-//
-//  SphereEntityItem.h
-//  libraries/entities/src
-//
-//  Created by Brad Hefta-Gaub on 12/4/13.
-//  Copyright 2013 High Fidelity, Inc.
-//
-//  Distributed under the Apache License, Version 2.0.
-//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-#ifndef hifi_SphereEntityItem_h
-#define hifi_SphereEntityItem_h
-
-#include "EntityItem.h"
-
-class SphereEntityItem : public EntityItem {
-public:
-    static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
-
-    SphereEntityItem(const EntityItemID& entityItemID);
-    
-    ALLOW_INSTANTIATION // This class can be instantiated
-    
-    // methods for getting/setting all properties of an entity
-    virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const;
-    virtual bool setProperties(const EntityItemProperties& properties);
-
-    virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const;
-
-    virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, 
-                                    EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
-                                    EntityPropertyFlags& requestedProperties,
-                                    EntityPropertyFlags& propertyFlags,
-                                    EntityPropertyFlags& propertiesDidntFit,
-                                    int& propertyCount, 
-                                    OctreeElement::AppendState& appendState) const;
-
-    virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, 
-                                                ReadBitstreamToTreeParams& args,
-                                                EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
-                                                bool& somethingChanged);
-
-    const rgbColor& getColor() const { return _color; }
-    xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; }
-
-    void setColor(const rgbColor& value) { memcpy(_color, value, sizeof(_color)); }
-    void setColor(const xColor& value) {
-            _color[RED_INDEX] = value.red;
-            _color[GREEN_INDEX] = value.green;
-            _color[BLUE_INDEX] = value.blue;
-    }
-
-    virtual ShapeType getShapeType() const { return SHAPE_TYPE_SPHERE; }
-    virtual bool shouldBePhysical() const { return !isDead(); }
-    
-    virtual bool supportsDetailedRayIntersection() const { return true; }
-    virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
-                                                bool& keepSearching, OctreeElementPointer& element, float& distance,
-                                                BoxFace& face, glm::vec3& surfaceNormal,
-                                                void** intersectedObject, bool precisionPicking) const;
-
-    virtual void debugDump() const;
-
-protected:
-
-    rgbColor _color;
-};
-
-#endif // hifi_SphereEntityItem_h
diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp
index 15bf44744c..6852d17882 100644
--- a/libraries/render-utils/src/GeometryCache.cpp
+++ b/libraries/render-utils/src/GeometryCache.cpp
@@ -112,10 +112,12 @@ void GeometryCache::ShapeData::drawWireInstances(gpu::Batch& batch, size_t count
     }
 }
 
+// The golden ratio
+static const float PHI = 1.61803398874f;
+
 const VertexVector& icosahedronVertices() {
-    static const float phi = (1.0f + sqrtf(5.0f)) / 2.0f;
-    static const float a = 0.5f;
-    static const float b = 1.0f / (2.0f * phi);
+    static const float a = 1;
+    static const float b = PHI / 2.0f;
 
     static const VertexVector vertices{ //
         vec3(0, b, -a), vec3(-b, a, 0), vec3(b, a, 0), // 
@@ -143,11 +145,10 @@ const VertexVector& icosahedronVertices() {
 }
 
 const VertexVector& tetrahedronVertices() {
-    static const float a = 1.0f / sqrtf(2.0f);
-    static const auto A = vec3(0, 1, a);
-    static const auto B = vec3(0, -1, a);
-    static const auto C = vec3(1, 0, -a);
-    static const auto D = vec3(-1, 0, -a);
+    static const auto A = vec3(1, 1, 1);
+    static const auto B = vec3(1, -1, -1);
+    static const auto C = vec3(-1, 1, -1);
+    static const auto D = vec3(-1, -1, 1);
     static const VertexVector vertices{
         A, B, C,
         D, B, A,
@@ -356,7 +357,7 @@ void GeometryCache::buildShapes() {
                 for (size_t j = 0; j < VERTICES_PER_TRIANGLE; ++j) {
                     auto triangleVertexIndex = j;
                     auto vertexIndex = triangleStartIndex + triangleVertexIndex;
-                    vertices.push_back(glm::normalize(originalVertices[vertexIndex]));
+                    vertices.push_back(originalVertices[vertexIndex]);
                     vertices.push_back(faceNormal);
                 }
             }
@@ -437,7 +438,7 @@ void GeometryCache::buildShapes() {
                 for (int j = 0; j < VERTICES_PER_TRIANGLE; ++j) {
                     auto triangleVertexIndex = j;
                     auto vertexIndex = triangleStartIndex + triangleVertexIndex;
-                    vertices.push_back(glm::normalize(originalVertices[vertexIndex]));
+                    vertices.push_back(originalVertices[vertexIndex]);
                     vertices.push_back(faceNormal);
                     indices.push_back((uint16_t)(vertexIndex + startingIndex));
                 }
@@ -1801,10 +1802,10 @@ uint32_t toCompactColor(const glm::vec4& color) {
 
 static const size_t INSTANCE_COLOR_BUFFER = 0;
 
-void renderInstances(const std::string& name, gpu::Batch& batch, const glm::vec4& color, bool isWire,
+void renderInstances(gpu::Batch& batch, const glm::vec4& color, bool isWire,
                     const render::ShapePipelinePointer& pipeline, GeometryCache::Shape shape) {
     // Add pipeline to name
-    std::string instanceName = name + std::to_string(std::hash<render::ShapePipelinePointer>()(pipeline));
+    std::string instanceName = (isWire ? "wire_shapes_" : "solid_shapes_") + std::to_string(shape) + "_" + std::to_string(std::hash<render::ShapePipelinePointer>()(pipeline));
 
     // Add color to named buffer
     {
@@ -1826,14 +1827,16 @@ void renderInstances(const std::string& name, gpu::Batch& batch, const glm::vec4
     });
 }
 
+void GeometryCache::renderSolidShapeInstance(gpu::Batch& batch, GeometryCache::Shape shape, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) {
+    renderInstances(batch, color, false, pipeline, shape);
+}
+
 void GeometryCache::renderSolidSphereInstance(gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) {
-    static const std::string INSTANCE_NAME = __FUNCTION__;
-    renderInstances(INSTANCE_NAME, batch, color, false, pipeline, GeometryCache::Sphere);
+    renderInstances(batch, color, false, pipeline, GeometryCache::Sphere);
 }
 
 void GeometryCache::renderWireSphereInstance(gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) {
-    static const std::string INSTANCE_NAME = __FUNCTION__;
-    renderInstances(INSTANCE_NAME, batch, color, true, pipeline, GeometryCache::Sphere);
+    renderInstances(batch, color, true, pipeline, GeometryCache::Sphere);
 }
 
 // Enable this in a debug build to cause 'box' entities to iterate through all the
@@ -1841,8 +1844,6 @@ void GeometryCache::renderWireSphereInstance(gpu::Batch& batch, const glm::vec4&
 //#define DEBUG_SHAPES
 
 void GeometryCache::renderSolidCubeInstance(gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) {
-    static const std::string INSTANCE_NAME = __FUNCTION__;
-    
 #ifdef DEBUG_SHAPES
     static auto startTime = usecTimestampNow();
     renderInstances(INSTANCE_NAME, batch, color, pipeline, [](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) {
@@ -1876,11 +1877,11 @@ void GeometryCache::renderSolidCubeInstance(gpu::Batch& batch, const glm::vec4&
         }
     });
 #else
-    renderInstances(INSTANCE_NAME, batch, color, false, pipeline, GeometryCache::Cube);
+    renderInstances(batch, color, false, pipeline, GeometryCache::Cube);
 #endif
 }
 
 void GeometryCache::renderWireCubeInstance(gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) {
     static const std::string INSTANCE_NAME = __FUNCTION__;
-    renderInstances(INSTANCE_NAME, batch, color, true, pipeline, GeometryCache::Cube);
+    renderInstances(batch, color, true, pipeline, GeometryCache::Cube);
 }
diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h
index c4531aa102..7fa543abe2 100644
--- a/libraries/render-utils/src/GeometryCache.h
+++ b/libraries/render-utils/src/GeometryCache.h
@@ -163,6 +163,13 @@ public:
     void renderShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer);
     void renderWireShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer);
 
+    void renderSolidShapeInstance(gpu::Batch& batch, Shape shape, const glm::vec4& color = glm::vec4(1),
+                                    const render::ShapePipelinePointer& pipeline = _simplePipeline);
+    void renderSolidShapeInstance(gpu::Batch& batch, Shape shape, const glm::vec3& color,
+                                    const render::ShapePipelinePointer& pipeline = _simplePipeline) {
+        renderSolidShapeInstance(batch, shape, glm::vec4(color, 1.0f), pipeline);
+    }
+
     void renderSolidSphereInstance(gpu::Batch& batch, const glm::vec4& color,
                                     const render::ShapePipelinePointer& pipeline = _simplePipeline);
     void renderSolidSphereInstance(gpu::Batch& batch, const glm::vec3& color,
diff --git a/scripts/developer/tests/entityEditStressTest.js b/scripts/developer/tests/entityEditStressTest.js
index 2d3c8ad0e1..8fa06a968d 100644
--- a/scripts/developer/tests/entityEditStressTest.js
+++ b/scripts/developer/tests/entityEditStressTest.js
@@ -15,161 +15,11 @@
 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-var NUM_ENTITIES = 20000;           // number of entities to spawn
-var ENTITY_SPAWN_LIMIT = 1000;
-var ENTITY_SPAWN_INTERVAL = 0.1;
+Script.include("./entitySpawnTool.js");
 
-var UPDATE_INTERVAL = 0.05;  // Re-randomize the entity's position every x seconds / ms
-var ENTITY_LIFETIME = 30;   // Entity timeout (when/if we crash, we need the entities to delete themselves)
-var KEEPALIVE_INTERVAL = 5; // Refreshes the timeout every X seconds 
-
-var RADIUS = 5.0;   // Spawn within this radius (square)
-var Y_OFFSET = 1.5; // Spawn at an offset below the avatar
-var TEST_ENTITY_NAME = "EntitySpawnTest";
-    
-(function () {
-    this.makeEntity = function (properties) {
-        var entity = Entities.addEntity(properties);
-        // print("spawning entity: " + JSON.stringify(properties));
-
-        return {
-            update: function (properties) {
-                Entities.editEntity(entity, properties);
-            },
-            destroy: function () {
-                Entities.deleteEntity(entity)
-            },
-            getAge: function () {
-                return Entities.getEntityProperties(entity).age;
-            }
-        };
-    }
-
-    this.randomPositionXZ = function (center, radius) {
-        return {
-            x: center.x + (Math.random() * radius * 2.0) - radius,
-            y: center.y,
-            z: center.z + (Math.random() * radius * 2.0) - radius
-        };
-    }
-    this.randomColor = function () {
-        var shade = Math.floor(Math.random() * 255);
-        var hue   = Math.floor(Math.random() * (255 - shade));
-
-        return {
-            red: shade + hue,
-            green: shade,
-            blue: shade
-        };
-    }
-    this.randomDimensions = function () {
-        return {
-            x: 0.1 + Math.random() * 0.5,
-            y: 0.1 + Math.random() * 0.1,
-            z: 0.1 + Math.random() * 0.5
-        };
-    }
-})();
-
-(function () {
-    var entities = [];
-    var entitiesToCreate = 0;
-    var entitiesSpawned = 0;
-
-
-    function clear () {
-        var ids = Entities.findEntities(MyAvatar.position, 50);
-        var that = this;
-        ids.forEach(function(id) {
-            var properties = Entities.getEntityProperties(id);
-            if (properties.name == TEST_ENTITY_NAME) {
-                Entities.deleteEntity(id);
-            }
-        }, this);
-    }    
-    
-    function createEntities () {
-        print("Creating " + NUM_ENTITIES + " entities (UPDATE_INTERVAL = " + UPDATE_INTERVAL + ", KEEPALIVE_INTERVAL = " + KEEPALIVE_INTERVAL + ")");
-        entitiesToCreate = NUM_ENTITIES;
-        Script.update.connect(spawnEntities);
-    }
-
-    var spawnTimer = 0.0;
-    function spawnEntities (dt) {
-        if (entitiesToCreate <= 0) {
-            Script.update.disconnect(spawnEntities);
-            print("Finished spawning entities");
-        } 
-        else if ((spawnTimer -= dt) < 0.0){
-            spawnTimer = ENTITY_SPAWN_INTERVAL;
-
-            var n = Math.min(entitiesToCreate, ENTITY_SPAWN_LIMIT);
-            print("Spawning " + n + " entities (" + (entitiesSpawned += n) + ")");
-
-            entitiesToCreate -= n;
-
-            var center = MyAvatar.position;
-            center.y -= Y_OFFSET;
-
-            for (; n > 0; --n) {
-                entities.push(makeEntity({
-                    type: "Box",
-                    name: TEST_ENTITY_NAME,
-                    position: randomPositionXZ(center, RADIUS),
-                    color: randomColor(),
-                    dimensions: randomDimensions(),
-                    lifetime: ENTITY_LIFETIME
-                }));
-            }
-        }
-    }
-
-    function despawnEntities () {
-        print("despawning entities");
-        entities.forEach(function (entity) {
-            entity.destroy();
-        });
-        entities = [];
-    }
-
-    var keepAliveTimer = 0.0;
-    var updateTimer = 0.0;
-
-    // Runs the following entity updates:
-    // a) refreshes the timeout interval every KEEPALIVE_INTERVAL seconds, and
-    // b) re-randomizes its position every UPDATE_INTERVAL seconds.
-    // This should be sufficient to crash the client until the entity tree bug is fixed (and thereafter if it shows up again).
-    function updateEntities (dt) {
-        var updateLifetime = ((keepAliveTimer -= dt) < 0.0) ? ((keepAliveTimer = KEEPALIVE_INTERVAL), true) : false;
-        var updateProperties = ((updateTimer -= dt) < 0.0) ? ((updateTimer = UPDATE_INTERVAL), true) : false;
-
-        if (updateLifetime || updateProperties) {
-            var center = MyAvatar.position;
-            center.y -= Y_OFFSET;
-
-            entities.forEach((updateLifetime && updateProperties && function (entity) {
-                entity.update({
-                    lifetime: entity.getAge() + ENTITY_LIFETIME,
-                    position: randomPositionXZ(center, RADIUS)
-                });
-            }) || (updateLifetime && function (entity) {
-                entity.update({
-                    lifetime: entity.getAge() + ENTITY_LIFETIME
-                });
-            }) || (updateProperties && function (entity) {
-                entity.update({
-                    position: randomPositionXZ(center, RADIUS)
-                });
-            }) || null, this);
-        }
-    }
-
-    function init () {
-        Script.update.disconnect(init);
-        clear();
-        createEntities();
-        Script.update.connect(updateEntities);
-        Script.scriptEnding.connect(despawnEntities);
-    }
-    Script.update.connect(init);
-})();
\ No newline at end of file
+ENTITY_SPAWNER({
+    count: 20000,
+    spawnLimit: 1000,
+    spawnInterval: 0.1,
+    updateInterval: 0.05
+});
diff --git a/scripts/developer/tests/entitySpawnTool.js b/scripts/developer/tests/entitySpawnTool.js
new file mode 100644
index 0000000000..d88933b867
--- /dev/null
+++ b/scripts/developer/tests/entitySpawnTool.js
@@ -0,0 +1,184 @@
+// entityEditStressTest.js
+//
+// Created by Seiji Emery on 8/31/15
+// Copyright 2015 High Fidelity, Inc.
+//
+// Stress tests the client + server-side entity trees by spawning huge numbers of entities in
+// close proximity to your avatar and updating them continuously (ie. applying position edits), 
+// with the intent of discovering crashes and other bugs related to the entity, scripting, 
+// rendering, networking, and/or physics subsystems.
+//
+// This script was originally created to find + diagnose an a clientside crash caused by improper
+// locking of the entity tree, but can be reused for other purposes.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+ENTITY_SPAWNER = function (properties) {
+    properties = properties || {};
+    var RADIUS = properties.radius || 5.0;   // Spawn within this radius (square)
+    var Y_OFFSET = properties.yOffset || 1.5; // Spawn at an offset below the avatar
+    var TEST_ENTITY_NAME = properties.entityName || "EntitySpawnTest";
+
+    var NUM_ENTITIES = properties.count || 1000; // number of entities to spawn
+    var ENTITY_SPAWN_LIMIT = properties.spawnLimit || 100;
+    var ENTITY_SPAWN_INTERVAL = properties.spawnInterval || properties.interval || 1.0;
+
+    var UPDATE_INTERVAL = properties.updateInterval || properties.interval || 0.1;  // Re-randomize the entity's position every x seconds / ms
+    var ENTITY_LIFETIME = properties.lifetime || 30;   // Entity timeout (when/if we crash, we need the entities to delete themselves)
+    var KEEPALIVE_INTERVAL = properties.keepAlive || 5; // Refreshes the timeout every X seconds
+    var UPDATES = properties.updates || false
+    var SHAPES = properties.shapes || ["Icosahedron", "Tetrahedron", "Cube", "Sphere" ];
+
+    function makeEntity(properties) {
+        var entity = Entities.addEntity(properties);
+        // print("spawning entity: " + JSON.stringify(properties));
+
+        return {
+            update: function (properties) {
+                Entities.editEntity(entity, properties);
+            },
+            destroy: function () {
+                Entities.deleteEntity(entity)
+            },
+            getAge: function () {
+                return Entities.getEntityProperties(entity).age;
+            }
+        };
+    }
+
+    function randomPositionXZ(center, radius) {
+        return {
+            x: center.x + (Math.random() * radius * 2.0) - radius,
+            y: center.y,
+            z: center.z + (Math.random() * radius * 2.0) - radius
+        };
+    }
+    
+    function randomPosition(center, radius) {
+        return {
+            x: center.x + (Math.random() * radius * 2.0) - radius,
+            y: center.y + (Math.random() * radius * 2.0) - radius,
+            z: center.z + (Math.random() * radius * 2.0) - radius
+        };
+    }
+    
+    
+    function randomColor() {
+        return {
+            red: Math.floor(Math.random() * 255),
+            green: Math.floor(Math.random() * 255),
+            blue: Math.floor(Math.random() * 255),
+        };
+    }
+    
+    function randomDimensions() {
+        return {
+            x: 0.1 + Math.random() * 0.5,
+            y: 0.1 + Math.random() * 0.1,
+            z: 0.1 + Math.random() * 0.5
+        };
+    }
+
+    var entities = [];
+    var entitiesToCreate = 0;
+    var entitiesSpawned = 0;
+    var spawnTimer = 0.0;
+    var keepAliveTimer = 0.0;
+    var updateTimer = 0.0;
+
+    function clear () {
+        var ids = Entities.findEntities(MyAvatar.position, 50);
+        var that = this;
+        ids.forEach(function(id) {
+            var properties = Entities.getEntityProperties(id);
+            if (properties.name == TEST_ENTITY_NAME) {
+                Entities.deleteEntity(id);
+            }
+        }, this);
+    }    
+    
+    function createEntities () {
+        print("Creating " + NUM_ENTITIES + " entities (UPDATE_INTERVAL = " + UPDATE_INTERVAL + ", KEEPALIVE_INTERVAL = " + KEEPALIVE_INTERVAL + ")");
+        entitiesToCreate = NUM_ENTITIES;
+        Script.update.connect(spawnEntities);
+    }
+
+    function spawnEntities (dt) {
+        if (entitiesToCreate <= 0) {
+            Script.update.disconnect(spawnEntities);
+            print("Finished spawning entities");
+        } 
+        else if ((spawnTimer -= dt) < 0.0){
+            spawnTimer = ENTITY_SPAWN_INTERVAL;
+
+            var n = Math.min(entitiesToCreate, ENTITY_SPAWN_LIMIT);
+            print("Spawning " + n + " entities (" + (entitiesSpawned += n) + ")");
+
+            entitiesToCreate -= n;
+
+            var center = MyAvatar.position;
+            center.y -= Y_OFFSET;
+
+            for (; n > 0; --n) {
+                entities.push(makeEntity({
+                    type: "Shape",
+                    shape: SHAPES[n % SHAPES.length],
+                    name: TEST_ENTITY_NAME,
+                    position: randomPosition(center, RADIUS),
+                    color: randomColor(),
+                    dimensions: randomDimensions(),
+                    lifetime: ENTITY_LIFETIME
+                }));
+            }
+        }
+    }
+
+    function despawnEntities () {
+        print("despawning entities");
+        entities.forEach(function (entity) {
+            entity.destroy();
+        });
+        entities = [];
+    }
+
+    // Runs the following entity updates:
+    // a) refreshes the timeout interval every KEEPALIVE_INTERVAL seconds, and
+    // b) re-randomizes its position every UPDATE_INTERVAL seconds.
+    // This should be sufficient to crash the client until the entity tree bug is fixed (and thereafter if it shows up again).
+    function updateEntities (dt) {
+        var updateLifetime = ((keepAliveTimer -= dt) < 0.0) ? ((keepAliveTimer = KEEPALIVE_INTERVAL), true) : false;
+        var updateProperties = ((updateTimer -= dt) < 0.0) ? ((updateTimer = UPDATE_INTERVAL), true) : false;
+
+        if (updateLifetime || updateProperties) {
+            var center = MyAvatar.position;
+            center.y -= Y_OFFSET;
+
+            entities.forEach((updateLifetime && updateProperties && function (entity) {
+                entity.update({
+                    lifetime: entity.getAge() + ENTITY_LIFETIME,
+                    position: randomPosition(center, RADIUS)
+                });
+            }) || (updateLifetime && function (entity) {
+                entity.update({
+                    lifetime: entity.getAge() + ENTITY_LIFETIME
+                });
+            }) || (updateProperties && function (entity) {
+                entity.update({
+                    position: randomPosition(center, RADIUS)
+                });
+            }) || null, this);
+        }
+    }
+
+    function init () {
+        Script.update.disconnect(init);
+        clear();
+        createEntities();
+        Script.update.connect(updateEntities);
+        Script.scriptEnding.connect(despawnEntities);
+    }
+    
+    Script.update.connect(init);
+};
\ No newline at end of file
diff --git a/scripts/developer/tests/primitivesTest.js b/scripts/developer/tests/primitivesTest.js
new file mode 100644
index 0000000000..e401963a83
--- /dev/null
+++ b/scripts/developer/tests/primitivesTest.js
@@ -0,0 +1,24 @@
+// entityEditStressTest.js
+//
+// Created by Seiji Emery on 8/31/15
+// Copyright 2015 High Fidelity, Inc.
+//
+// Stress tests the client + server-side entity trees by spawning huge numbers of entities in
+// close proximity to your avatar and updating them continuously (ie. applying position edits), 
+// with the intent of discovering crashes and other bugs related to the entity, scripting, 
+// rendering, networking, and/or physics subsystems.
+//
+// This script was originally created to find + diagnose an a clientside crash caused by improper
+// locking of the entity tree, but can be reused for other purposes.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+Script.include("./entitySpawnTool.js");
+
+
+ENTITY_SPAWNER({
+    updateInterval: 2.0
+})
+
diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html
index efe7e6cc65..4a3b5a14a4 100644
--- a/scripts/system/html/entityProperties.html
+++ b/scripts/system/html/entityProperties.html
@@ -25,6 +25,7 @@
         var ICON_FOR_TYPE = {
             Box: "V",
             Sphere: "n",
+            Shape: "n",
             ParticleEffect: "&#xe004;",
             Model: "&#xe008;",
             Web: "q",
@@ -403,6 +404,10 @@
                 var elColorGreen = document.getElementById("property-color-green");
                 var elColorBlue = document.getElementById("property-color-blue");
     
+                var elShapeSections = document.querySelectorAll(".shape-section");
+                allSections.push(elShapeSections);
+                var elShape = document.getElementById("property-shape");
+                
                 var elLightSections = document.querySelectorAll(".light-section");
                 allSections.push(elLightSections);
                 var elLightSpotLight = document.getElementById("property-light-spot-light");
@@ -666,7 +671,18 @@
                                     elHyperlinkSections[i].style.display = 'table';
                                 }
 
-                                if (properties.type == "Box" || properties.type == "Sphere" || properties.type == "ParticleEffect") {
+                                if (properties.type == "Shape" || properties.type == "Box" || properties.type == "Sphere") {
+                                    for (var i = 0; i < elShapeSections.length; i++) {
+                                        elShapeSections[i].style.display = 'table';
+                                    }
+                                } else {
+                                    for (var i = 0; i < elShapeSections.length; i++) {
+                                        console.log("Hiding shape section " + elShapeSections[i])
+                                        elShapeSections[i].style.display = 'none';
+                                    }
+                                }
+                                
+                                if (properties.type == "Shape" || properties.type == "Box" || properties.type == "Sphere" || properties.type == "ParticleEffect") {
                                     for (var i = 0; i < elColorSections.length; i++) {
                                         elColorSections[i].style.display = 'table';
                                     }
@@ -958,6 +974,8 @@
                 elLightExponent.addEventListener('change', createEmitNumberPropertyUpdateFunction('exponent', 2));
                 elLightCutoff.addEventListener('change', createEmitNumberPropertyUpdateFunction('cutoff', 2));
     
+                elShape.addEventListener('change', createEmitTextPropertyUpdateFunction('shape'));
+
                 elWebSourceURL.addEventListener('change', createEmitTextPropertyUpdateFunction('sourceUrl'));
     
                 elModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('modelURL'));
@@ -1344,6 +1362,36 @@
             <span id="property-id" class="selectable"></span>
         </div>
 
+        <div class="section-header shape-group shape-section">
+            <label>Shape</label><span>M</span>
+        </div>
+        <div class="shape-group shape-section property dropdown">
+            <label for="property-shape">Shape</label>
+            <select name="SelectShape" id="property-shape">
+                <option value="Cube">Cube</option>
+                <option value="Sphere">Sphere</option>
+                <option value="Icosahedron">Icosahedron</option>
+                <option value="Tetrahedron">Tetrahedron</option>
+            </select>
+        </div>
+        <div class="shape-group shape-section property text">
+            <label>Temp</label>
+            <input type="text" id="property-temp-1">
+        </div>
+        <div class="shape-group shape-section property text">
+            <label>Temp</label>
+            <input type="text" id="property-temp-2">
+        </div>
+<!-- 
+        <div class="shape-group shape-section property text">
+            <label>Temp</label>
+            <input type="text" id="property-temp-3">
+        </div>
+        <div class="shape-group shape-section property text">
+            <label>Temp</label>
+            <input type="text" id="property-temp-4">
+        </div>
+ -->
 
         <div class="section-header hyperlink-group hyperlink-section">
             <label>Hyperlink</label><span>M</span>
@@ -1678,7 +1726,6 @@
             <textarea id="property-model-original-textures" readonly></textarea>
         </div>
 
-
         <div class="section-header text-group text-section">
             <label>Text</label><span>M</span>
         </div>
diff --git a/tests/entities/src/main.cpp b/tests/entities/src/main.cpp
index c0e21276d8..792ef7d9c6 100644
--- a/tests/entities/src/main.cpp
+++ b/tests/entities/src/main.cpp
@@ -16,7 +16,7 @@
 #include <QDir>
 #include <ByteCountCoding.h>
 
-#include <BoxEntityItem.h>
+#include <ShapeEntityItem.h>
 #include <EntityItemProperties.h>
 #include <Octree.h>
 #include <PathUtils.h>
@@ -155,7 +155,7 @@ int main(int argc, char** argv) {
     QFile file(getTestResourceDir() + "packet.bin");
     if (!file.open(QIODevice::ReadOnly)) return -1;
     QByteArray packet = file.readAll();
-    EntityItemPointer item = BoxEntityItem::factory(EntityItemID(), EntityItemProperties());
+    EntityItemPointer item = ShapeEntityItem::boxFactory(EntityItemID(), EntityItemProperties());
     ReadBitstreamToTreeParams params;
     params.bitstreamVersion = 33;