Skip to main content

Somato

Somato is a lightweight procedural body generation and rendering library for games and simulations.

It provides geometry-based building blocks for creating configurable body features from data.

Somato is designed to work with externally supplied parameters, making it suitable for:

  • Procedural character generation
  • Character customization
  • Species and race definitions
  • Simulation systems
  • Randomized appearances
  • Data-driven character creation

Version 0.1.0 currently focuses on procedural eyes.

Example Outputs

child_eye parent_1_eye parent_2_eye

Features

  • Cubic Bézier eye shapes
  • Configurable eye geometry
  • Iris rendering
  • Iris clipping to the eye shape
  • Eye scaling
  • Independent horizontal and vertical scaling
  • Eye rotation
  • Custom sclera and outline colors
  • Simple render pipeline
  • Pillow-based image output
  • Data-driven feature generation

Installation

Somato depends on Pillow.

Install dependencies with:

pip install -r requirements.txt

A minimal requirements.txt is:

Pillow

Basic Usage

Import the main Somato classes:

from somato import Somato, Eye, Iris

Create an iris:

iris = Iris(
	center=(200, 150),
	radius_x=35,
	color=(70, 130, 180)
)

Create an eye:

eye = Eye(
	p0=(50, 150),
	upper_p1=(120, 70),
	upper_p2=(280, 70),
	p3=(350, 150),
	lower_p1=(120, 230),
	lower_p2=(280, 230),
	thickness=5,
	iris=iris
)

Create a Somato canvas:

body = Somato(
	width=400,
	height=300
)

Add the eye:

body.add(
	eye
)

Save the result:

body.save(
	"eye.png"
)

Complete Example

from somato import Somato, Eye, Iris


iris = Iris(
	center=(200, 150),
	radius_x=35,
	radius_y=35,
	color=(80, 130, 75)
)


eye = Eye(
	p0=(50, 150),
	upper_p1=(120, 70),
	upper_p2=(280, 70),
	p3=(350, 150),
	lower_p1=(120, 230),
	lower_p2=(280, 230),
	thickness=5,
	iris=iris,
	sclera_color="white",
	outline_color="black"
)


somato = Somato(
	width=400,
	height=300,
	background_color="white"
)


somato.add(
	eye
)


somato.save(
	"eye.png"
)

Eye Geometry

An eye is created from two cubic Bézier curves.

The upper eyelid uses:

p0
upper_p1
upper_p2
p3

The lower eyelid uses:

p0
lower_p1
lower_p2
p3

Both curves share the same starting and ending points.

The two curves are combined into a closed shape that is filled with the sclera color.

Endpoints

p0 is the left endpoint of the eye.

p3 is the right endpoint of the eye.

For example:

p0=(50, 150)
p3=(350, 150)

These points determine the overall horizontal span of the eye.

Control Points

The control points determine the curvature of the upper and lower eyelids.

upper_p1=(120, 70)
upper_p2=(280, 70)

lower_p1=(120, 230)
lower_p2=(280, 230)

Moving these points changes the resulting eye shape.

Because the geometry is directly configurable, eye shapes can be created manually or generated from external data.

Cubic Bézier Curves

Somato uses cubic Bézier curves internally.

A cubic Bézier curve is calculated from four points:

P0
P1
P2
P3

Where:

P0 and P3 = endpoints
P1 and P2 = control points

Somato samples points along each curve and uses those points to construct the final eye shape.

You normally do not need to call the Bézier function directly when creating an eye.

Iris

An iris is represented by an ellipse.

iris = Iris(
	center=(200, 150),
	radius_x=35,
	radius_y=35,
	color="brown"
)

center

The center position of the iris:

center=(200, 150)

radius_x

The horizontal radius:

radius_x=35

radius_y

The vertical radius:

radius_y=35

If radius_y is omitted, it defaults to the same value as radius_x.

For example:

Iris(
	center=(200, 150),
	radius_x=35
)

creates a circular iris.

Different horizontal and vertical radii can create an elliptical iris:

Iris(
	center=(200, 150),
	radius_x=40,
	radius_y=30
)

color

The iris color can use any color format supported by Pillow.

For example:

color="brown"

or:

color=(70, 130, 180)

The iris is automatically clipped so that it does not render outside the eye shape.

Eye Appearance

The sclera color can be changed with:

sclera_color="white"

The outline color can be changed with:

outline_color="black"

The outline thickness is controlled by:

thickness=5

Example:

eye = Eye(
	p0=(50, 150),
	upper_p1=(120, 70),
	upper_p2=(280, 70),
	p3=(350, 150),
	lower_p1=(120, 230),
	lower_p2=(280, 230),
	thickness=8,
	sclera_color=(240, 240, 220),
	outline_color=(40, 40, 40)
)

Transformations

Eyes can be transformed without manually editing every Bézier point.

Somato currently supports:

scale
scale_x
scale_y
rotation

Scale

Uniformly scale the eye:

scale=1.25

Values above 1 enlarge the eye.

Values below 1 shrink it.

Horizontal Scale

Change only the horizontal size:

scale_x=1.5

Vertical Scale

Change only the vertical size:

scale_y=0.75

Rotation

Rotate the eye in degrees:

rotation=15

Example:

eye = Eye(
	p0=(50, 150),
	upper_p1=(120, 70),
	upper_p2=(280, 70),
	p3=(350, 150),
	lower_p1=(120, 230),
	lower_p2=(280, 230),
	thickness=5,
	scale=1.1,
	scale_x=1.2,
	scale_y=0.9,
	rotation=10
)

Transformations are applied around the center point between p0 and p3.

Rendering Multiple Body Parts

Somato stores renderable body parts in a list.

Add a body part with:

somato.add(
	body_part
)

When render() is called, each body part is drawn in the order it was added.

somato.render()

Calling:

somato.save(
	"output.png"
)

automatically renders the body parts before saving the image.

Data-Driven Generation

Somato does not determine how appearance values are created.

Instead, it accepts geometry and appearance data supplied by another part of the application.

For example:

eye_data = {
	"p0": (50, 150),
	"upper_p1": (120, 85),
	"upper_p2": (280, 75),
	"p3": (350, 140),
	"lower_p1": (120, 205),
	"lower_p2": (280, 190),
	"iris_color": (80, 130, 75)
}

That data can then be used to construct an eye:

iris = Iris(
	center=(200, 150),
	radius_x=35,
	color=eye_data["iris_color"]
)


eye = Eye(
	p0=eye_data["p0"],
	upper_p1=eye_data["upper_p1"],
	upper_p2=eye_data["upper_p2"],
	p3=eye_data["p3"],
	lower_p1=eye_data["lower_p1"],
	lower_p2=eye_data["lower_p2"],
	thickness=5,
	iris=iris
)

The data could come from any external system, including:

  • Procedural generation
  • Character creation
  • Species templates
  • Random generation
  • Simulation rules
  • Saved character data
  • Configuration files
  • External libraries

Somato is responsible only for turning those values into rendered body features.

Coordinate System

Somato uses Pillow's image coordinate system.

The origin is at the top-left corner:

(0, 0)

Increasing x moves to the right.

Increasing y moves downward.

For example:

(100, 50)

is farther right and higher on the image than:

(100, 200)

Somato Canvas

The Somato class creates the image used for rendering.

somato = Somato(
	width=800,
	height=400,
	background_color="white"
)

width

Image width in pixels.

height

Image height in pixels.

background_color

Background color used when creating the image.

Somato creates an RGBA image internally.

API Overview

Somato

Somato(
	width=800,
	height=400,
	background_color="white"
)

Methods:

add(body_part)
render()
save(file_name)

Eye

Eye(
	p0,
	upper_p1,
	upper_p2,
	p3,
	lower_p1,
	lower_p2,
	thickness,
	iris=None,
	sclera_color="white",
	outline_color="black",
	scale=1,
	scale_x=1,
	scale_y=1,
	rotation=0
)

Methods:

transform_point(point)
get_shape()
draw(image)

Iris

Iris(
	center,
	radius_x,
	radius_y=None,
	color="brown"
)

Methods:

bounds()

Design Philosophy

Somato separates appearance rendering from appearance generation.

The library does not decide why a character has a particular body feature.

It simply receives geometry and appearance values and renders them.

For example, an eye shape could come from:

  • A procedural generator
  • A character creator
  • A species definition
  • A simulation
  • Random generation
  • A saved character profile

Somato remains independent of the system that produced those values.

This makes it possible to use the same rendering system with many different character-generation approaches.

Current Limitations

Version 0.1.0 is an early proof of concept.

Currently:

  • Eyes are the only implemented body feature
  • Pupils are not yet implemented
  • Procedural parameters must be supplied by the application
  • Somato does not include character-generation or simulation logic

Additional body features may be added in future versions.

Version

Current version:

0.1.0

The API may change as Somato expands beyond the initial eye system.

Metadata

Release files for somato 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for somato 0.1.0
File Size Uploaded Provenance Actions
somato-0.1.0.tar.gz 9.4 kB Without provenance Details for somato-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for somato 0.1.0
File Interpreter ABI Platform Actions
somato-0.1.0-py3-none-any.whl Python 3 none any Details for somato-0.1.0-py3-none-any.whl

Total release size: 16.4 kB

Release files / somato-0.1.0.tar.gz

Download URL somato-0.1.0.tar.gz
Size 9.4 kB
Tags Source
SHA-256 checksum
How to use checksums
e76aa6d1005eac43a2416af820bd15f2dc8da5ce2a780b2761e1a3b6a39e4c37
BLAKE2b-256 checksum
How to use checksums
4f424fb53681dc099325accff3bfd8358af00c99026ee839ba01a6922ccacf3d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.7

Release files / somato-0.1.0-py3-none-any.whl

Download URL somato-0.1.0-py3-none-any.whl
Size 7.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3f3b5091025b98790cc2f6b9f13f10dda1f8300a7b604ebcdba4042c826ad0ad
BLAKE2b-256 checksum
How to use checksums
79f3e2b67e196dc54e1f0f44ad871df4ef91545c1c25286afeb6e5ddae7a3d68
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.7

Security for somato 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page