Shapes2D for Unity

Thanks for using Shapes2D, a Unity asset for easily creating art assets in the Unity editor. You can use Shapes2D for creating PNG sprites, prototyping, game jams, UI, effects and more!

Shapes2D is available in the Unity asset store.

Getting Help

Check out the forums or email oliver@sub-c.org. Follow me on Twitter for occasional progress updates.


  • Extremely easy to use
  • Shapes can act as sprites or Unity GUI objects
  • Convert to Sprite button can turn shapes into a .PNG with one click
  • Multiple layered shapes can be collapsed into a single .PNG
  • Works with Unity GUI masking

Shapes can optionally be used procedurally at runtime (see Performance), meaning:

  • Shapes can be scaled up without becoming pixellated
  • Shapes can be modified/animated at runtime using the Scripting API

Base shape types are:

  • Circles/Ellipses (with settings for donuts, arcs, pies and wedges)
  • Paths (made of quadratic Bézier curves, up to 32 segments, optionally fill closed areas)
  • Polygons (convex or concave, up to 64 vertices)
  • Rectangles (and rounded rectangles)
  • Triangles

Customizations include:

  • Outlines
  • Blur
  • Gradient fills
  • Texture fills
  • Pattern fills (grids, stripes & checkerboard)


After importing Shapes2D, go to GameObject->Shapes2D and choose one of the template Shapes. Once your Shape is created, you can tweak its various options from the Shape component in the Inspector.

In the Shape component, sizes are typically specified in world units and outlines will keep the same thickness no matter how they are scaled.

Unity GUI

To use Shapes2D with canvas game objects, just add a Shape component to them. In Unity, canvases may be scaled quite a lot to fit your screen, depending on your settings. That means the scale of units may be very different than when dealing with sprites (canvases do use world units, which is why they appear so huge in the editor, but they are scaled down by Unity at runtime to overlay your screen). So, you may have to scale values (like outline size) up quite a lot to get the same effect in a UI component as with a sprite.


If you set blur to 0, Shapes2D will add resolution-independent anti-aliasing to Shapes so that they will look smooth regardless of scaling - essentially it is just modifying the blur value on the fly as you scale in and out. If you set blur to some value greater than 0, then blur is specified in world units and will stay fixed.


Shapes can act like sprites or UI components, and will obey the same sorting rules. For sprite-based Shapes, use the attached SpriteRenderer component to specify the sorting layer and order. For UI components, the parent canvas has sorting options and the children will render in the order they appear in the hierarchy.

Convert to Sprite

To convert a Shape component to a sprite, press the “Convert to Sprite” button in the Shape component in the inspector. A PNG asset will be created in a folder called /Assets/Resources/Shapes2D Sprites, and the object will be converted into a SpriteRenderer or Image, depending on the object. Any active child Shapes will also be combined into the PNG, allowing you to create complex layered Shapes. To revert the sprite conversion, either use Unity’s Undo function or just re-enable the Shape component and any child Shapes.


Shapes2D defaults to 100 pixels per world unit to determine the size of the generated PNG, so if your Shape is 1x2 world units, a 100x200 pixel PNG will be created. However, for UI Shapes where the parent canvas is not in world mode, Shapes2D assumes the size in world units is already specified in pixels and just uses that value divided by the canvas’s scaleFactor.

You can change the pixels per world unit setting by going to /Shapes2D/Preferences in your Project pane (assuming you kept the default import location).

Scaling UI

For rectangular UI components, after converting to a sprite you may want to convert the resulting PNG into a 9-Slice so your component can scale without having its borders stretched. To do that, just select the PNG asset in the Project window and use Unity’s Sprite Editor to bring in the borders a little (the green bars in the Sprite Editor). Hit Apply, and then change the Shape’s Image component to have an Image Type of “Sliced”. Now Unity will be able to tile the parts of the image outside the borders instead of stretching them.


If you convert your Shapes to sprites, then Shapes2D has no impact on the performance of your game.

However, before converting to sprite, Shapes are rendered on the fly using shaders, which is why they look so smooth when scaled up and can be changed at runtime via scripting. For simple Shapes like rectangles that isn’t such a big deal, but for complex shape types like polygons and paths it can be really expensive. Lots of math has to be done for each pixel, so the size of the Shape on the screen matters.

To make things worse, Shapes do not batch and require one Material per Shape. Changing any Shape properties via scripting causes properties to be re-sent to the GPU which is also a performance hit. Mobile in particular does not have very good performance, depending on what you’re doing.

Nevertheless, using the included Stress Test demo I am able to get 1000+ non-complex Shapes on-screen at once at 60fps on my MacBook Pro. Dynamically modifying Shape properties at runtime is slower, and I get about 500+ Shapes running at 60fps. On mobile, performance is much worse and I recommend testing on all target platforms before attempting to use Shapes2D procedurally. You should be able to get away with small numbers of non-complex shapes.

If performance becomes an issue, a good workflow is to rapidly create Shapes when prototyping and then either “Convert to Sprite” or replace them with artist-created assets later. You should test on each of your supported platforms and decide if you can afford to use a procedural Shape or if you should replace prototype Shapes with sprites (or perhaps an animated mesh, for certain types of effects).

Path Performance

Paths (even line paths) are the most expensive Shape type for performance, and I recommend always converting them to sprites unless you are very sure your target platform can handle it. The more segments you use, the more expensive rendering will be. If you do need dynamic procedural curves or paths at runtime, you might want to look into a vector solution which would convert your paths into a much more performance-friendly mesh. There are other assets on the Unity Asset Store for that!

On mobile, performance for paths is very bad - convert them to sprites first!

Polygon Optimization

Polygon Shapes have an additional “optimized” rendering mode which can be selected via a checkbox in the Unity Component Inspector.

Optimized polygons:

  • Are about as fast as the other Shape types, even on mobile
  • Are slow if you want to update their vertices dynamically via code (and slow to Instantiate)
  • Will render fine for most polygons but will show rendering artifacts if vertices are too close to each other, if vertices are too close to polygon edges, or if polygon edges intersect each other

Non-optimized polygons:

  • Are slow in general, and slower the more vertices you have
  • Are very slow on mobile
  • Can have their vertices updated quickly
  • Should always render correctly

If you intend to Convert to Sprite anyway, go ahead and leave your polygons non-optimized so you don’t have to worry about rendering issues.

Supported Platforms

Shapes2D has been tested on OSX, Windows, iOS, Android and WebGL. For polygons and paths with high numbers of vertices, Shapes2D shaders may not compile for the Direct3D9 Graphics API, meaning you should not target that platform if you intend to use complex shapes procedurally (converting to sprite is fine because the shaders aren’t required at runtime). If you notice any problems with Shapes not appearing or appearing wrong, please let me know!

Scripting API

To modify Shapes from code, you can do things like this:

Shapes2D.Shape shape = GetComponent<Shapes2D.Shape>();
shape.settings.outlineSize += Time.deltaTime;
shape.settings.outlineColor = new Color(1, 1, 1, 1);


If you want to modify a Shape’s attributes from your Start() method, you may need to add a call to shape.ComputeAndApply() to prevent a flicker of the Shape’s initial attributes on the first frame.

See below for customizable settings.

API Documentation


The Shape Component

UserProps (customizable settings)


enum Shapes2D::ShapeType

Shapes2D base shape types.


enum Shapes2D::FillType

Shapes2D fill types.


enum Shapes2D::GradientType

Shapes2D gradient fill types.


enum Shapes2D::GradientAxis

Shapes2D gradient fill direction.


enum Shapes2D::PolygonPreset

Shapes2D polygon presets.



The Shape Component

class Shapes2D::Shape

The MonoBehaviour for a Shapes2D Shape. Can be used with either a SpriteRenderer or an Image. If the Object does not have either component, one will be added depending on if there is a Canvas in the parent hierarchy or not. This component will also replace the Material and Sprite of an existing SpriteRenderer or Image attached to the GameObject.

Inherits from MonoBehaviour, IMaterialModifier

Public Functions

void Shapes2D.Shape.ComputeAndApply(bool disableBlending = false)

Used internally by Shape and ShapeEditor. Computes and sends the Shape‘s attributes to the shader. Normally you don’t need to call this (even when changing settings on the fly), but you may need to call this manually to force an update if you change settings from Start() to avoid a flicker on the first frame.

Public Members

UserProps Shapes2D.Shape.settings

Shape attributes that can be modified at runtime (see UserProps).

UserProps (customizable settings)

class Shapes2D::Shape::UserProps

Runtime-editable attributes of a Shapes2D Shape.


property Shapes2D::Shape::UserProps::blur

The amount of blur in world units applied to the Shape. If the Shape has an outline, blur is applied to both the inner and outer parts of the outline. Set to 0 to have Shapes2D dynamically apply a small amount of blur that varies with the screen resolution.

property Shapes2D::Shape::UserProps::correctScaling

If shapeType is Ellipse and outlineSize > 0, enabling this will use a much more complicated (but glitchy) algorithm that tries to apply an equal thickness at all places along the outline. This only applies in cases when the algorithm decides it’s likely to be useful (very non-circular ellipses). With this disabled, a simpler algorithm is used that does a decent job, but outlines may have irregular thickness with thick outlines and very non-circular ellipses. I strongly recommend leaving this disabled unless you really need it, and are able to test that it works reliably in all situations and platforms you care about. The innerCutout and arcAngle properties do not work when correctScaling is turned on and the algorithm is applied.

property Shapes2D::Shape::UserProps::endAngle

If shapeType is Ellipse, specifies the end angle in degrees of the visible arc area. Only areas of the ellipse between startAngle and endAngle will be visible.

property Shapes2D::Shape::UserProps::fillColor

The Shape‘s fill color, depending on fillType.

property Shapes2D::Shape::UserProps::fillColor2

For certain fill types, this is the second fill color used.

property Shapes2D::Shape::UserProps::fillOffset

An offset applied to the fill in world units.

property Shapes2D::Shape::UserProps::fillPathLoops

If shapeType is Path, this will cause any loops in the path to be filled.

property Shapes2D::Shape::UserProps::fillRotation

The rotation of the fill, in degrees.

property Shapes2D::Shape::UserProps::fillScale

A scale applied to the fill.

property Shapes2D::Shape::UserProps::fillTexture

A Texture2D to use for the fill, if fillType is Texture.

property Shapes2D::Shape::UserProps::fillType

The Shape‘s fill type, a Shapes2D.FillType.

property Shapes2D::Shape::UserProps::gradientAxis

A Shapes2D.GradientAxis specifying the gradient direction, if the fillType is Gradient. The difference between this and simply using fillRotation to rotate a gradient fill is that Shapes2D uses the axis to determine the gradient’s start and end positions. When using a rotated gradient rather than specifying the axis directly, the gradient may not stretch the way you expect it to when you scale the Shape.

property Shapes2D::Shape::UserProps::gradientStart

The gradient’s start position as a percentage (from 0-1) along its gradientAxis, if the fillType is Gradient.

property Shapes2D::Shape::UserProps::gradientType

A Shapes2D.GradientType specifying the gradient type, if the fillType is Gradient.

property Shapes2D::Shape::UserProps::gridSize

If fillType is Grid, Checkerboard or Stripes, gridSize is the size in world units of the grid (or the distance between lines).

property Shapes2D::Shape::UserProps::innerCutout

If shapeType is Ellipse, setting this will cut out an inner “donut hole” of the ellipse. Each dimension is specified from 0-1 and is a percentage of the X and Y radii of the ellipse. Use by itself or in combination with startAngle and endAngle to turn a pie shape into an arc.

property Shapes2D::Shape::UserProps::invertArc

If shapeType is Ellipse, this inverts startAngle and endAngle so that they describe the invisible area rather than the visible area of the ellipse.

property Shapes2D::Shape::UserProps::lineSize

If fillType is Grid or Stripes, lineSize is the size in world units of the lines in the fill.

property Shapes2D::Shape::UserProps::outlineColor

The color of the outline, if any.

property Shapes2D::Shape::UserProps::outlineSize

Outline size in world units. Specifically, this is the thickness in world units of the shape’s outline on one side of the Shape.

property Shapes2D::Shape::UserProps::pathSegments

If shapeType is Path, this holds a list of PathSegments. A segment is a quadratic Bézier curve, with each curve made of 3 points. The first and last point in each segment will be clamped to the range -0.5, 0.5. To make a connected path, set points of connected segments to be equal to each other. You can have from 1 to MaxPathSegments (currently 32). If you modify the returned array directly rather than setting a new array, call settings.pathSegments = settings.pathSegments anyway so the shape knows to update the shader and values are sanitized.

property Shapes2D::Shape::UserProps::pathThickness

If shapeType is Path, this is the thickness of the path in world units.

property Shapes2D::Shape::UserProps::polygonPreset

For use with shapeType Polygon, sets the vertices to a copy from the given Shapes2D.PolygonPreset.

property Shapes2D::Shape::UserProps::polyVertices

If shapeType is Polygon, this holds its vertices normalized from -0.5 to 0.5 (verts will be clamped to this range). You can have from 3 to MaxPolygonVertices (currently 64). If you modify the returned vertex array directly rather than setting a new array, call settings.polyVertices = settings.polyVertices anyway so the shape knows to update the shader and values are sanitized.

property Shapes2D::Shape::UserProps::roundness

If shapeType is Rectangle and roundnessPerCorner is false, this specifies the amount of the rectangle, along a single side, that will be rounded. For UI shapes this is in world units so borders stay the same as you scale, like a 9-slice. For sprite shapes it’s a percentage of the available area so shapes won’t change as they scale.

property Shapes2D::Shape::UserProps::roundnessBottomLeft

If shapeType is Rectangle and roundnessPerCorner is true, this specifies the amount of the rectangle in world units, along a single side, that will be rounded.

property Shapes2D::Shape::UserProps::roundnessBottomRight

If shapeType is Rectangle and roundnessPerCorner is true, this specifies the amount of the rectangle in world units, along a single side, that will be rounded.

property Shapes2D::Shape::UserProps::roundnessPerCorner

If shapeType is Rectangle, set this to true if you want to use per-corner roundness attributes instead of a single value for all corners.

property Shapes2D::Shape::UserProps::roundnessTopLeft

If shapeType is Rectangle and roundnessPerCorner is true, this specifies the amount of the rectangle in world units, along a single side, that will be rounded.

property Shapes2D::Shape::UserProps::roundnessTopRight

If shapeType is Rectangle and roundnessPerCorner is true, this specifies the amount of the rectangle in world units, along a single side, that will be rounded.

property Shapes2D::Shape::UserProps::shapeType

The base shape type, a Shapes2D.ShapeType.

property Shapes2D::Shape::UserProps::startAngle

If shapeType is Ellipse, specifies the start angle in degrees of the visible arc area. Only areas of the ellipse between startAngle and endAngle will be visible.

property Shapes2D::Shape::UserProps::triangleOffset

If shapeType is Triangle, this is position on the x axis of the topmost point as a percentage of its width, from 0-1. It may be a little clumsy but you should be able to get any triangle you want by using triangleOffset and rotating the Shape.

property Shapes2D::Shape::UserProps::usePolygonMap

If shapeType is Polygon, enabling this will pre-compute some data allowing us to render much faster. Most polygons will render correctly using this optimization, but not all. In general, vertices cannot be too close to each other, and vertices cannot be too close to non-adjacent polygon edges, or you’ll see rendering issues. Also, polygon edges can’t intersect with each other. If you get good performance with leaving this off (or plan to Convert to Sprite anyway), then leave it off. On mobile, polygon rendering is very slow and you’ll probably need this optimization.