The G3Engine 2D Editor provides a complete toolset for building 2D games, pixel art, UI interfaces, and sprite-based content. Built with HTML5 Canvas, it offers real-time rendering with layer support and professional editing tools.
Overview
The 2D viewport uses a custom Canvas2D renderer with camera controls, layer management, and precision editing tools optimized for game development.
The 2D editor supports unlimited sprites, layers, and game canvas sizes up to 4096x4096 pixels.
Canvas Workspace
Game Area
Your game’s playable area is shown as a bordered region in the center of the viewport:
Default size : 800x600 pixels
Customizable dimensions : Configure in editor settings
Visual boundary : Dashed green outline marks the game area
Origin marker : Shows (0,0) coordinate at center
// From editor2DStore.ts:79-80
canvasWidth : 800 ,
canvasHeight : 600 ,
Camera System
Navigate your scene with intuitive camera controls:
Middle-click + drag to pan the camera
Alt + left-click + drag for one-handed panning
Camera position updates in real-time
Scroll wheel to zoom in/out
Zoom range: 0.1x to 5x
Zoom centers on cursor position
resetCamera() action returns to default view
Center: (0, 0)
Zoom: 1.0x
// Camera state from editor2DStore.ts:56
interface Camera2D {
x : number ; // Pan offset X
y : number ; // Pan offset Y
zoom : number ; // Zoom level (0.1 - 5.0)
}
Grid System
Precision placement with customizable grid:
Grid size : Default 32px, fully customizable
Snap to grid : Toggle with snapToGrid setting
Visual grid : Toggle with showGrid setting
Origin lines : Highlighted X/Y axes at (0,0)
// Grid rendering from Viewport2D.tsx:212-239
if ( showGrid ) {
ctx . strokeStyle = 'rgba(255,255,255,0.03)' ;
ctx . lineWidth = 1 / camera . zoom ;
// Draw grid lines at gridSize intervals
for ( let x = startX ; x <= endX ; x += gridSize ) {
ctx . beginPath ();
ctx . moveTo ( x , - halfH );
ctx . lineTo ( x , halfH );
ctx . stroke ();
}
}
Grid snapping applies to sprite movement and placement, making pixel-perfect alignment effortless.
Sprite System
Sprites are the building blocks of 2D scenes:
interface Sprite2D {
id : string ;
name : string ;
type : 'sprite' | 'shape' | 'text' | 'tilemap' ;
// Transform
x : number ;
y : number ;
width : number ;
height : number ;
rotation : number ; // Degrees
scaleX : number ;
scaleY : number ;
// Rendering
opacity : number ; // 0-1
visible : boolean ;
locked : boolean ;
layerId : string ;
// Appearance
fillColor : string ;
strokeColor : string ;
strokeWidth : number ;
// Type-specific
shapeType ?: 'rect' | 'circle' | 'line' | 'polygon' ;
cornerRadius ?: number ;
text ?: string ;
fontSize ?: number ;
fontFamily ?: string ;
emoji ?: string ; // Quick prototyping
}
Sprite Types
Shapes
Text
Emoji Sprites
Vector shapes with fill and stroke:
Rectangle : With optional corner radius
Circle/Ellipse : Perfect for particles, bullets
Line : For connections, trails
Polygon : Custom shapes
// Shape rendering from Viewport2D.tsx:260-276
if ( sp . fillColor && sp . fillColor !== 'transparent' ) {
ctx . fillStyle = sp . fillColor ;
ctx . beginPath ();
if ( sp . shapeType === 'circle' ) {
ctx . ellipse ( 0 , 0 , hw , hh , 0 , 0 , Math . PI * 2 );
} else {
ctx . roundRect ( - hw , - hh , sp . width , sp . height , sp . cornerRadius || 0 );
}
ctx . fill ();
}
Render text labels and UI:
Custom fonts via fontFamily
Adjustable fontSize
Fill color customization
Center-aligned by default
if ( sp . type === 'text' && sp . text ) {
ctx . fillStyle = sp . fillColor || '#f0f0f5' ;
ctx . font = ` ${ sp . fontSize || 16 } px ${ sp . fontFamily || 'Inter, sans-serif' } ` ;
ctx . textAlign = 'center' ;
ctx . textBaseline = 'middle' ;
ctx . fillText ( sp . text , 0 , 0 );
}
Quick prototyping with emoji:
Single emoji per sprite
Auto-sized to fit sprite bounds
Perfect for placeholders
if ( sp . emoji ) {
const emojiSize = Math . min ( sp . width , sp . height ) * 0.7 ;
ctx . font = ` ${ emojiSize } px serif` ;
ctx . textAlign = 'center' ;
ctx . textBaseline = 'middle' ;
ctx . fillText ( sp . emoji , 0 , 2 );
}
Layer Management
Organize sprites with a professional layer system:
interface Layer2D {
id : string ;
name : string ;
visible : boolean ; // Show/hide entire layer
locked : boolean ; // Prevent editing
opacity : number ; // Layer-wide opacity (0-1)
order : number ; // Render order (higher = on top)
}
Default Layers
G3Engine provides three starter layers:
Background (order: 0) - For backgrounds and environments
Main (order: 1) - Primary game content
UI (order: 2) - User interface elements
Create Layer
Call addLayer(name) to create a new layer. It’s automatically placed on top.
Reorder Layers
Use reorderLayer(id, newOrder) to change render order. Higher order renders on top.
Layer Visibility
Toggle visible to show/hide all sprites on a layer without deleting them.
Lock Layer
Set locked: true to prevent accidental edits while keeping layer visible.
Layer Rendering
// From Viewport2D.tsx:242-248
const visibleLayers = allLayers
. filter (( l ) => l . visible )
. sort (( a , b ) => a . order - b . order );
for ( const layer of visibleLayers ) {
const layerSprites = allSprites . filter (( sp ) =>
sp . layerId === layer . id && sp . visible
);
// Render sprites with combined opacity
ctx . globalAlpha = sp . opacity * layer . opacity ;
}
Sprite opacity is multiplied by layer opacity for fine-grained control.
The 2D editor provides multiple tools for different tasks:
Tool Hotkey Description Select VSelect and click sprites Move GDrag sprites to reposition Draw BFree-hand drawing Erase ERemove pixels or sprites Shape UCreate geometric shapes Text TAdd text labels
// Tool type from editor2DStore.ts:8
type Tool2D = 'select' | 'move' | 'draw' | 'erase' | 'fill' | 'shape' | 'text' ;
When a sprite is selected:
Green outline : Dashed border indicates selection
Corner handles : Four handles for future resize support
Drag to move : Click and drag to reposition
Grid snapping : Automatic if snapToGrid is enabled
// Selection rendering from Viewport2D.tsx:298-322
if ( sp . id === selectedSpriteId ) {
ctx . save ();
ctx . translate ( sp . x , sp . y );
ctx . rotate (( sp . rotation * Math . PI ) / 180 );
// Selection outline
ctx . strokeStyle = '#14f195' ;
ctx . lineWidth = 2 / camera . zoom ;
ctx . setLineDash ([ 6 / camera . zoom , 3 / camera . zoom ]);
ctx . strokeRect ( - hw - 2 , - hh - 2 , sp . width + 4 , sp . height + 4 );
// Corner handles
const handleSize = 6 / camera . zoom ;
ctx . fillStyle = '#14f195' ;
const corners = [
[ - hw - 2 , - hh - 2 ], [ hw + 2 , - hh - 2 ],
[ - hw - 2 , hh + 2 ], [ hw + 2 , hh + 2 ],
];
for ( const [ cx , cy ] of corners ) {
ctx . fillRect ( cx - handleSize / 2 , cy - handleSize / 2 , handleSize , handleSize );
}
ctx . restore ();
}
Hit Testing
The editor uses precise bounding box hit detection:
// From Viewport2D.tsx:64-80
for ( let i = sprites . length - 1 ; i >= 0 ; i -- ) {
const sp = sprites [ i ];
// Check layer visibility and lock state
if ( ! sp . visible || ! visibleLayers . has ( sp . layerId ) || lockedLayers . has ( sp . layerId ))
continue ;
// Bounding box test
if (
world . x >= sp . x - sp . width / 2 &&
world . x <= sp . x + sp . width / 2 &&
world . y >= sp . y - sp . height / 2 &&
world . y <= sp . y + sp . height / 2
) {
selectSprite ( sp . id );
return ;
}
}
Hit testing respects layer order - sprites on higher layers are tested first.
Keyboard Shortcuts
Rapid workflow with comprehensive shortcuts:
Shortcut Action VSelect tool GMove tool BDraw/Brush tool EErase tool UShape tool TText tool Delete / BackspaceDelete selected sprite Cmd/Ctrl + ZUndo Cmd/Ctrl + Shift + ZRedo
Coordinate System
The 2D editor uses a center-origin coordinate system:
Origin (0, 0) : Center of the game canvas
+X : Right
+Y : Down (standard canvas convention)
Sprite positions : Center-anchored by default
Screen to World Conversion
// From Viewport2D.tsx:26-31
const screenToWorld = ( sx : number , sy : number ) => {
return {
x: ( sx - camera . x ) / camera . zoom ,
y: ( sy - camera . y ) / camera . zoom ,
};
};
World to Screen Conversion
// From Viewport2D.tsx:33-39
const worldToScreen = ( wx : number , wy : number ) => {
return {
x: wx * camera . zoom + camera . x ,
y: wy * camera . zoom + camera . y ,
};
};
Rendering Pipeline
The 2D editor uses a requestAnimationFrame render loop:
Clear Canvas
Fill with dark background color #12121c
Apply Camera Transform
Translate by camera position and scale by zoom level
Render Game Area
Draw background and dashed border for game bounds
Draw Grid
Render grid lines if showGrid is enabled
Render Sprites by Layer
Loop through visible layers in order, rendering sprites with transforms and opacity
Draw Selection
Render selection outline and handles for selected sprite
HUD Overlays
Canvas dimensions label, origin marker
// Main render loop from Viewport2D.tsx:164-346
const render = () => {
// Resize canvas to container
const { width : cw , height : ch } = container . getBoundingClientRect ();
canvas . width = cw ;
canvas . height = ch ;
// Clear
ctx . fillStyle = '#12121c' ;
ctx . fillRect ( 0 , 0 , cw , ch );
// Apply camera
ctx . save ();
ctx . translate ( cam . x , cam . y );
ctx . scale ( cam . zoom , cam . zoom );
// ... render scene ...
ctx . restore ();
frameId = requestAnimationFrame ( render );
};
History System
Full undo/redo support for all sprite operations:
// History structure from editor2DStore.ts:83-84
history : { sprites : Sprite2D [] }[];
historyIndex : number ;
Auto-save : History snapshots after add, remove, move, update
Unlimited undo : Navigate back through all changes
State restoration : Sprite properties fully restored on undo/redo
Optimized for smooth 60fps rendering:
Efficient Canvas2D : Direct pixel manipulation
Layer culling : Hidden layers skip rendering
Camera frustum : Only visible sprites processed (future optimization)
Transform caching : Reuse calculations where possible
Next Steps
3D Editor Switch to 3D mode for immersive worlds
Visual Scripting Add interactivity to your 2D sprites
Asset Library Import and manage 2D assets
Web3 Integration Tokenize 2D sprites as NFTs