Skip to content

AI Image Generator — Java source

Turn a text prompt into a 1024×1024 image with FLUX.1 [schnell] on Cloudflare Workers AI. No account, no API key — rate-limited for fair use. Your prompt goes to the model through our Worker; we store no prompts and no images.

This is the Java implementation — the same logic the interactive tool runs, in a shareable, citable form.

// AI Image Generator — prompt validation, usage-gate verdicts, and ?prompt=
// URL state for FLUX.1-schnell image generation.
//
// Language: Java (Java 17, standard library only)
// Source:   CosmoDev polyglot showcase port, from src/lib/image-gen.ts.

import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneOffset;

public final class ImageGen {
    public static final String MODEL = "@cf/black-forest-labs/flux-1-schnell";
    public static final int STEPS = 4; // flux-1-schnell's design point -> 57.6 neurons/image
    public static final int PROMPT_MAX_CHARS = 600; // UX clamp, below the 2048 max
    public static final int IP_HOUR_CAP = 10;
    public static final int DAILY_CAP = 100;

    /** ok ? prompt : null; reason is null when ok ("empty"|"tooLong"|"controlChars"). */
    public record PromptResult(boolean ok, String prompt, String reason) {}

    /** ok ? null error : "RATE_LIMITED_IP"|"RATE_LIMITED_DAILY" + retry minutes. */
    public record GateVerdict(boolean ok, String error, int retryAfterMinutes) {}

    /** Normalize CRLF/CR to \n, trim, enforce 1..600 chars, and reject control
     *  chars except \n (C0 minus U+000A, DEL, C1). */
    public static PromptResult validatePrompt(String raw) {
        String prompt = raw.replace("\r\n", "\n").replace('\r', '\n').strip();
        if (prompt.isEmpty()) return new PromptResult(false, null, "empty");
        if (prompt.codePointCount(0, prompt.length()) > PROMPT_MAX_CHARS) {
            return new PromptResult(false, null, "tooLong");
        }
        for (int i = 0; i < prompt.length(); i++) {
            char c = prompt.charAt(i);
            boolean isCtrl = c < '\n' || (c > '\n' && c < ' ') || (c >= 0x7F && c <= 0x9F);
            if (isCtrl) return new PromptResult(false, null, "controlChars");
        }
        return new PromptResult(true, prompt, null);
    }

    /** Minutes (>= 1) until the next UTC midnight — the daily window reset. */
    private static int minutesToUtcMidnight(Instant now) {
        var midnight = LocalDate.ofInstant(now, ZoneOffset.UTC).plusDays(1)
                .atStartOfDay().toInstant(ZoneOffset.UTC);
        return Math.max(1, (int) Math.ceil(Duration.between(now, midnight).toMillis() / 60_000.0));
    }

    /** Daily cap wins: the site-global budget resets at UTC midnight. */
    public static GateVerdict checkGate(int ipCount, int dailyCount, Instant now) {
        if (dailyCount >= DAILY_CAP) {
            return new GateVerdict(false, "RATE_LIMITED_DAILY", minutesToUtcMidnight(now));
        }
        if (ipCount >= IP_HOUR_CAP) return new GateVerdict(false, "RATE_LIMITED_IP", 60);
        return new GateVerdict(true, null, 0);
    }

    /** Encode the shareable state: "" when there is nothing to share.
     *  URLEncoder is form-encoded (+ for spaces) — swap in %20 to match the
     *  TS encodeURIComponent. */
    public static String encodeUrlState(String prompt) {
        if (prompt.isEmpty()) return "";
        return "?prompt=" + URLEncoder.encode(prompt, StandardCharsets.UTF_8).replace("+", "%20");
    }

    /** Inverse of encodeUrlState — a missing param decodes to "". */
    public static String decodeUrlState(String search) {
        for (String pair : search.replaceFirst("^\\?", "").split("&")) {
            if (pair.startsWith("prompt=")) {
                String value = pair.substring("prompt=".length()).replace("%20", "+");
                return URLDecoder.decode(value, StandardCharsets.UTF_8);
            }
        }
        return "";
    }

    private ImageGen() {}
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →