Klip - Kotlin Language Image Processing


This is one of my recent AI-assisted projects where I usedClaude/ChatGPT to explore what kind of code I could get it to write. Overall, I'm very happy with the results. Source can be found on GitHub.


Klip is a Kotlin-based image and canvas processing server that dynamically transforms images stored in AWS S3. Powered by GraphicsMagick, it delivers fast, scalable, and customizable image manipulation through HTTP GET requests.

Features

  • Image Transformations – Resize, crop, rotate, grayscale, blur, sharpen, and more.
  • Canvas Generation – Create custom canvases with gradients, patterns, text overlays, borders, and rounded corners.
  • Flexible Rules Engine – Enforce size, quality, and filter constraints with configurable rules for enhanced security and optimization.
  • Caching Support – Seamlessly caches transformed images to reduce processing overhead and improve performance.
  • AWS S3 Integration – Pulls source images directly from S3 and supports caching to a separate bucket.
  • Admin APIs – Monitor performance and track cache statistics with secure, API-key-protected admin endpoints.
  • Highly Configurable – Customize behavior using environment variables for features, limits, and resource constraints.

Endpoints

  • /klip – Transform existing images stored in AWS S3.
  • /canvas – Generate custom graphics and placeholders with flexible styling options.

Get Image

GET /klip/{path/to/image}

Fetch the original image stored in the S3 bucket without any transformations.

Example:

GET http://localhost:8080/klip/properties/1/04c08449e126.png

Resize Image

GET /klip/{path/to/image}?w={width}&h={height}

# Set width and height independently.
GET /klip/{path/to/image}?w={width}
GET /klip/{path/to/image}?h={height}

# Dimension syntax
GET /klip/{path/to/image}?d={width}x{height}

Resize the image to the specified width and height.

Query Parameters:

ParameterTypeRequiredDefaultDescription
widthIntOptional-Width of the output image
heightIntOptional-Height of the output image

Example:

GET http://localhost:8080/klip/properties/1/04c08449e126.png?d=640x480
GET http://localhost:8080/klip/properties/1/04c08449e126.png?w=640&h480

Resized Image

Fit Modes

Control how the image fits within the specified dimensions.

GET /klip/{path/to/image}?w={width}&h={height}&fit={mode}
ParameterTypeRequiredDefaultDescription
fitStringOptional-How the image should fit within dimensions. Values: contain, cover, fill

Fit Modes Explained:

contain: Preserves aspect ratio while ensuring image fits within specified dimensions

GET /klip/photo.jpg?w=800&h=600&fit=contain

cover: Fills entire dimensions while preserving aspect ratio, cropping excess

GET /klip/photo.jpg?w=800&h=600&fit=cover

fill: Stretches or squishes image to exactly fit dimensions

GET /klip/photo.jpg?w=800&h=600&fit=fill

Default Behavior:

  • With both width and height: Acts as fill
  • With single dimension: Acts as contain

Quality Adjustment

GET /klip/{path/to/image}?quality=75

Adjust the image quality for compression and size optimization. Useful for reducing image size without significant loss of visual fidelity.

Query Parameters:

ParameterTypeRequiredDefaultDescription
qualityIntNo100Adjusts image quality (1–100). Lower values reduce size but may lose detail.

Supported Formats: JPEG, PNG, WebP (other formats may default to lossless encoding).

Examples:

# High Quality (Default)
GET http://localhost:8080/klip/properties/1/04c08449e126.png?quality=100
# Medium-High Quality
GET http://localhost:8080/klip/properties/102/05013ad4469e00a7aed9596bc37af74e.jpg?quality=75
# Medium Quality
GET http://localhost:8080/klip/properties/102/05013ad4469e00a7aed9596bc37af74e.jpg?quality=50
# Low Quality
GET http://localhost:8080/klip/properties/102/05013ad4469e00a7aed9596bc37af74e.jpg?quality=25
# Lower Quality
GET http://localhost:8080/klip/properties/102/05013ad4469e00a7aed9596bc37af74e.jpg?quality=10
# Combined with resizing
GET http://localhost:8080/klip/properties/102/05013ad4469e00a7aed9596bc37af74e.jpg?quality=10&w1301h781

Image Comparison (click to enlarge)

High (100) (default)Medium-High (75)Medium (50)
100%75%75%
991,091 bytes205,236 bytes132,963 bytes
Low (25)Lower (10)
25%25%
83,156 bytes43,615 bytes

Center Crop

GET /klip/{path/to/image}?crop?w={width}&h={height}

Crop the image from the center to the specified width and height.

Query Parameters:

ParameterTypeRequiredDefaultDescription
cropBooleanNofalseCrops the image to fit the size.

Example:

GET http://localhost:8080/klip/properties/1/04c08449e126.png?crop&w=250&h=250

Cropped Image


Grayscale Filter

GET /klip/{path/to/image}?grayscale

Convert the image to grayscale while resizing to the specified dimensions.

Query Parameters:

ParameterTypeRequiredDefaultDescription
grayscaleBooleanNofalseApplies a grayscale filter to the image.

Example:

GET http://localhost:8080/klip/250x250/properties/1/04c08449e126.png?grayscale

Grayscale Image


Flip Horizontally

GET /klip/{path/to/image}?flipH

Flip the image horizontally (left-to-right).

Query Parameters:

ParameterTypeRequiredDefaultDescription
flipHBooleanNofalseFlips the image horizontally (left-to-right).

Example:

GET http://localhost:8080/klip/properties/1/04c08449e126.png?flipH

Flipped Horizontally


Flip Vertically

GET /klip/{path/to/image}?flipV

Flip the image vertically (top-to-bottom).

Query Parameters:

ParameterTypeRequiredDefaultDescription
flipVBooleanNofalseFlips the image vertically (top-to-bottom).

Example - Flip vertically:

GET http://localhost:8080/klip/properties/1/04c08449e126.png?flipV

Flipped Vertically

Example - Flip both horizontally and vertically:

GET http://localhost:8080/klip/properties/1/04c08449e126.png?flipH&flipV

Flipped Both


Rotate Image

GET /klip/{path/to/image}?rotate=45

Rotate the image by the specified degrees (clockwise).

Query Parameters:

ParameterTypeRequiredDefaultDescription
rotateFloatNo0Rotates the image by the specified angle.

Example - Rotate image 45 degrees:

GET http://localhost:8080/klip/properties/1/04c08449e126.png?rotate=45

Rotated Image


Blur Image

GET /klip/{path/to/image}?blur={radius}x{sigma}
GET /klip/{path/to/image}?blur={blur}

Apply a Gaussian blur to the image to soften details.

Query Parameters:

ParameterTypeRequiredDefaultDescription
blurIntNo0Simple blur with a single integer, range: [1, 10].
blurStringNo0Fine-tune Gaussian blur, format: {radius}x{sigma}.

blur format mappings:

BlurRadiusSigmaDescription
110.5Very light blur
221.0Light blur
331.5Moderate blur
442.0Strong blur
552.5Very strong blur
10105.0Extreme blur (background effects)
  • radius: Defines the area of the blur effect (higher = wider blur).
  • sigma: Controls the strength of the blur (higher = softer edges).

Example - Mild blur:

GET http://localhost:8080/klip/properties/1/04c08449e126.png?blur=0x2

Example: Simple, heavy blur

GET http://localhost:8080/klip/properties/1/04c08449e126.png?blur=7

Blurred Image


Sharpen Image

GET /klip/{path/to/image}?sharpen=2.0

Sharpen the image to enhance details and make edges clearer.

Query Parameters:

ParameterTypeRequiredDefaultDescription
sharpenFloatNo0Applies sharpening in the format radiusxsigma.
  • radius: Defines the area of the blur effect (higher = wider blur).
  • sigma: Controls the strength of the blur (higher = softer edges).

Example - Mild blur:

GET http://localhost:8080/klip/properties/1/04c08449e126.png?sharpen=2.0

Blurred Image


Color Adjustment

GET /klip/{path/to/image}?colors=16

Reduce the number of colors in the image to simplify or stylize it.

Query Parameters:

ParameterTypeRequiredDefaultDescription
colorsIntNo256Reduces the image to the specified color count.

Example - 10 colors:

GET http://localhost:8080/klip/properties/1/04c08449e126.png?colors=10

Blurred Image


Dithering

GET /klip/{path/to/image}?dither

Enable or disable dithering to improve color approximation when reducing colors.

Query Parameters:

ParameterTypeRequiredDefaultDescription
ditherBooleanNofalseEnable dithering.

Example - 10 colors:

GET http://localhost:8080/klip/properties/1/04c08449e126.png?dither

Combine Filters

GET /klip/{path/to/image}?grayscale&crop&rotate=90

Apply multiple transformations in a single request.

Example:

GET http://localhost:8080/klip/properties/1/04c08449e126.png?w=250&h=250&grayscale&crop&rotate=90

Combined Filters


Health Check

GET /health

Check if the server is up and running.

Response:

{
  "status": "UP"
}

Version Check

GET /version

Get the current version of the Klip server.

Response:

1.0.0

Admin Status Check

Get detailed status information about Klip

GET /admin/status

with Authorization: ApiKey <YOUR_API_KEY>

Response:

{
    "totalRequests": 504,
    "cacheHits": 252,
    "cacheHitRate": 0.5,
    "pool": {
        "maxConcurrent": 2,
        "currentAvailable": 2,
        "totalProcessed": 281,
        "averageWaitMs": 20049
    }
}

Canvas - Generated Placeholder Images

GET /canvas/{width}x{height}

Generate a placeholder image with custom dimensions, background color, and optional text overlay.

Query Parameters:

ParameterTypeRequiredDefaultDescription
bgColorStringOptionalgrayBackground color (name or hex code)
textColorStringOptionalwhiteText color (name or hex code)
textSizeIntOptional20Font size for the text
textStringOptional-Text to display on the canvas
patternStringOptional-Pattern type: "check", "grid", "stripe"
patternSizeIntOptional20-40Size of pattern elements
gradientStringOptional-Gradient spec: "blue,red" or "45,#ff0000,blue"
fontStringOptionalArialFont family for text
textAlignStringOptionalcenterText alignment: "center", "north", "southwest"
borderIntOptional-Border width in pixels
borderColorStringOptional-Border color (name or hex code)
radiusIntOptional0Corner radius for rounded corners
grayscaleBoolOptionalfalseConvert to grayscale
qualityIntOptional-Output image quality (1-100)
blurStringOptional-Blur effect: "2.5" or "2.5x1.2"
sharpenFloatOptional-Sharpen effect intensity
rotateFloatOptional-Rotation angle in degrees
flipHBoolOptionalfalseFlip horizontally
flipVBoolOptionalfalseFlip vertically

Examples:

EndpointDescription
Canvas/canvas/300x200?text=Hello&bgColor=%23336699&textColor=white
Canvas/canvas/320x320?text=Hello&textSize=40&textColor=white&pattern=check&patternSize=32&border=10&borderColor=black
Canvas/canvas/320x320?text=Hello&textSize=40&borderColor=black&border=10&textColor=white&gradient=blue%2Cred
Canvas/canvas/320x320?pattern=grid&patternSize=50&text=Grid

More Examples:

# Basic gray placeholder
GET /canvas/640x480

# Blue placeholder with text
GET /canvas/200x100?bgColor=blue&text=Hello

# Custom colors and font size
GET /canvas/400x300?bgColor=%23FF0000&textColor=white&text=Preview&textSize=30

# Using hex colors
GET /canvas/300x200?bgColor=%23336699&text=Loading

# More
GET /canvas/300x200?bg=blue&text=Hello
GET /canvas/300x200?gradient=45,blue,red&text=Gradient
GET /canvas/300x200?pattern=check&patternSize=30
GET /canvas/300x200?pattern=grid&patternSize=50&text=Grid
GET /canvas/300x200?bg=white&border=5&borderColor=black&radius=10
GET /canvas/300x200?text=Hello&font=Helvetica&align=north&grayscale=1

Klip Rules Configuration

Klip allows you to configure transformation rules for image processing using either environment variables or a rules file. These rules enforce constraints on transformations, ensuring only allowed operations are applied.


Environment Variable Configuration

Set rules using the KLIP_RULES and KLIP_CANVAS_RULES environment variable as a semicolon-separated string:

Example:

KLIP_RULES="+flipV;-flipH;dim 32x32 64x64 128x128;blur 1 2 3 4;quality 75 85 90;rotate 0 90 180"
KLIP_CANVAS_RULES="+flipV;-flipH;dim 32x32 64x64 128x128;rotate 0 90 180"

File-Based Configuration

Alternatively, rules can be stored in a rules.txt file and referenced via the KLIP_RULES_FILE environment variable.

Example:

KLIP_RULES_FILE=/app/config/rules.txt
KLIP_CANVAS_RULES_FILE=/app/config/canvas_rules.txt

rules.txt

+flipV
-flipH
dim 32x32 64x64 128x128
blur 1 2 3 4
quality 75 85 90
rotate 0 90 180

Rule Syntax

RuleDescriptionExample
+flipVAllow vertical flipping.+flipV
-flipVDisallow vertical flipping.-flipV
+flipHAllow horizontal flipping.+flipH
-flipHDisallow horizontal flipping.-flipH
+grayscaleAllow grayscale conversion.+grayscale
-grayscaleDisallow grayscale conversion.-grayscale
dim {WxH}Allow specific dimensions (width x height).dim 32x32 64x64 128x128
blur {values}Allow specific blur radii.blur 1 2 3 4
quality {values}Allow specific quality settings.quality 75 85 90
rotate {values}Allow specific rotation angles in degrees.rotate 0 90 180
sharpen {values}Allow specific sharpen values.sharpen 0.5 1.0 2.0
colors {values}Allow specific color palette sizes.colors 16 32 64 128
fit {values}Allow specific fit values .fit cover contain

Usage in Docker

Environment Variable Example:

docker run -e KLIP_RULES="+flipV;-flipH;dim 32x32 64x64 128x128" klip-app

File Example:

docker run -e KLIP_RULES_FILE=/config/rules.txt -v /local/config:/config klip-app

Testing Rules Locally

To test rules without deployment:

Create a rules.txt file:

+flipV
-flipH
dim 32x32 64x64 128x128
blur 1 2 3 4
quality 75 85 90
rotate 0 90 180

Use the following shell script:

KLIP_RULES_FILE=./klip_rules.txt ./gradlew run

Alternatively, set directly as an environment variable:

KLIP_RULES="+flipV;-flipH;dim 32x32 64x64 128x128" ./gradlew run

Errors

GET /klip/properties/1/04c08449e1261fedc2eb1a6a99245531.png?w=10&h=9
GET /klip/properties/1/04c08449e1261fedc2eb1a6a99245531.png?d=10x9

422 - Unprocessable Entity

{
  "error": "Dimensions must be > 10. Got: 10x9"
}

Installation

Prerequisites

  • Java 21+
  • Gradle
  • GraphicsMagick
brew install graphicsmagick

Build Project

./gradlew clean build

Environment Configuration (Env)

SectionVariableTypeDefaultRequiredDescription
FeaturesKLIP_ENABLEDBooleantrueNoEnable/disable the /klip/ endpoint for image transformations.
FeaturesKLIP_CANVAS_ENABLEDBooleantrueNoEnable/disable the /canvas/ endpoint for image generation.
FeaturesKLIP_ADMIN_ENABLEDBooleanfalseNoEnable/disable the admin endpoints.
SecurityKLIP_ADMIN_API_KEYString-Yes*API key required for admin endpoints when admin is enabled.
LOGKLIP_LOG_LEVELEnumINFONoTRACE, INFO, DEBUG, WARN, ERROR
HTTPKLIP_HTTP_PORTInt8080NoThe HTTP port the server listens on.
AWSKLIP_AWS_REGIONString-YesAWS region for S3 bucket (e.g., us-west-2).
AWSKLIP_S3_BUCKETString-YesThe S3 bucket name where source images are stored.
CacheKLIP_CACHE_ENABLEDBooleanTrueNoIf false, disable image cache.
CacheKLIP_CACHE_BUCKETStringSame as KLIP_S3_BUCKETNoUsed if using different S3 bucket for caching.
CacheKLIP_CACHE_FOLDERString_cache/NoPrefix for cached files. Stored within the cache bucket.
RulesKLIP_RULESString"" (empty)NoInline rule definitions separated by ; (e.g., +flipV;-flipH;dim 32x32 64x64).
RulesKLIP_RULES_FILEString-NoPath to a rules file with one rule per line. Overrides KLIP_RULES.
GraphicsMagickKLIP_GM_TIMEOUT_SECONDSLong30NoMaximum time in seconds for a GraphicsMagick operation.
GraphicsMagickKLIP_GM_MEMORY_LIMITString256MBNoMemory limit for GraphicsMagick operations.
GraphicsMagickKLIP_GM_MAP_LIMITString512MBNoMemory map limit for GraphicsMagick operations.
GraphicsMagickKLIP_GM_DISK_LIMITString1GBNoDisk space limit for GraphicsMagick operations.
GraphicsMagickKLIP_GM_POOL_SIZEIntAvailable ProcessorsNoMaximum number of concurrent GraphicsMagick operations.

Run Klip Server

KLIP_ENABLED=true \
KLIP_CANVAS_ENABLED=true \
KLIP_ADMIN_ENABLED=true \
KLIP_ADMIN_API_KEY="your.secret.key" \
KLIP_LOG_LEVEL=INFO \
KLIP_HTTP_PORT=8080 \
KLIP_AWS_REGION=us-west-2 \
KLIP_S3_BUCKET=cdn.klip.com \
KLIP_CACHE_ENABLED=true \
KLIP_CACHE_BUCKET=cdn.klip.com \
KLIP_CACHE_FOLDER=.cache/ \
KLIP_RULES="+flipV;-flipH;dim 32x32 64x64 256x256" \
KLIP_GM_TIMEOUT_SECONDS=30 \
KLIP_GM_MEMORY_LIMIT=256MB \
KLIP_GM_MAP_LIMIT=512MB \
KLIP_GM_DISK_LIMIT=1GB \
KLIP_GM_POOL_SIZE=4 \
java -jar build/libs/klip-all.jar

Default local endpoint: http://0.0.0.0:8080


Docker

Build the project

./gradlew clean build

Create Dockerfile from template (one-time step)

cp Dockerfile.template Dockerfile

Afterward set the ENV variables as needed.

Build Docker image

docker build --platform linux/amd64 -t klip-prod:latest .

Tag and Deploy image

docker tag klip-prod:latest <AWS_ACCOUNT_ID>.dkr.ecr.<AWS_REGION>.amazonaws.com/klip-prod:latest
docker push <AWS_ACCOUNT_ID>.dkr.ecr.<AWS_REGION>.amazonaws.com/klip-prod:latest

Force deploy of Klip

aws ecs update-service \
  --cluster klip-prod-cluster \
  --service klip-prod-service \
  --force-new-deployment

Tail Logs

aws logs tail /ecs/klip-prod --follow

Terraform

ALB, ECS, Fargate, ECR

Note that you'll likely need to adjust this terraform to fit your project. This is meant as a starting point.

Setup

cd terraform/stacks/
cp prod-template prod

Make whatever edits you need to the terraform/variables.

cd terraform/stacks/prod/
terraform init
terraform apply

Roadmap

  • Configurable Secret Key to protect admin endpoints (clear cache, get stats)
  • Migrate to Kotlin Native after (aws s3 client is for kotlin jvm)
  • Configurable backend storage (S3 vs File)