Skip to main content
Glama

asset_generate_splash_screen

Generate splash screen bundles for iOS, Android, and PWA from a brand mark SVG. Choose external prompt or API mode to composite the mark onto a background color and output platform-specific assets.

Instructions

Generate a cross-platform splash-screen bundle from a brand mark. Two modes (external_prompt_only / api); inline_svg is not supported (splash screens are PNG bundles — generate a logo inline_svg first, then call this with existing_mark_svg). api mode composites the mark onto background_color and emits ios/LaunchScreen-2732.png, android/mipmap-*dpi/splash.png, android/themes-splash.xml, pwa/splash-1200.png, and a README describing how to wire each.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
briefYes
modeNo
brand_bundleNo
existing_mark_svgNoPath to an existing brand-mark SVG to center on the splash. Preferred over regenerating.
platformsNo
background_colorNo
output_dirNo

Implementation Reference

  • Main handler function `generateSplashScreen` that executes the splash screen generation logic: resolves mode, generates or reads a 1024×1024 brand mark SVG/PNG, composites it centered at 40% onto a flat background color, and fans out platform-specific PNGs (iOS 2732×2732, Android mipmap-*dpi 192-768px, PWA 1200×1200) plus themes-splash.xml and README.md.
    export async function generateSplashScreen(
      input: GenerateSplashScreenInputT
    ): Promise<AssetGenerationResult> {
      const spec = await enhancePrompt({
        brief: input.brief,
        asset_type: "splash_screen",
        ...(input.brand_bundle && { brand_bundle: input.brand_bundle })
      });
    
      const { mode } = resolveMode(
        input.mode,
        "splash_screen",
        spec.target_model,
        spec.fallback_models
      );
    
      if (mode === "inline_svg") {
        throw new Error(
          "mode=inline_svg is not supported for asset_generate_splash_screen — splash screens are platform-specific PNG bundles. Generate a logo inline_svg first, save it, then call this tool with existing_mark_svg pointing at the saved SVG."
        );
      }
    
      if (mode === "external_prompt_only") {
        return buildExternalPromptPlan("splash_screen", input.brief, spec);
      }
    
      // api mode: produce a 1024² master mark, then fan out to platform sizes.
      const outDir = safeWritePath(
        input.output_dir ?? resolve(CONFIG.outputDir, `splash-${Date.now()}`)
      );
      mkdirSync(outDir, { recursive: true });
    
      const background = input.background_color ?? input.brand_bundle?.palette?.[0] ?? "#ffffff";
      const warnings: string[] = [...spec.warnings];
    
      const sharp = await loadSharp();
      if (!sharp) {
        throw new Error(
          "sharp is required for splash screen generation. Install the optional dep: `npm install sharp`."
        );
      }
    
      // 1. Get the 1024² mark.
      let markPng: Buffer;
      let modelUsed = "splash-composite";
      let seed = 0;
      let prompt_hash = "";
      let params_hash = "";
    
      if (input.existing_mark_svg) {
        const svgText = readFileSync(safeReadPath(input.existing_mark_svg), "utf-8");
        assertSafeSvg(svgText);
        markPng = await sharp(Buffer.from(svgText), { density: 600 })
          .resize(1024, 1024, { fit: "contain", background: { r: 0, g: 0, b: 0, alpha: 0 } })
          .png()
          .toBuffer();
      } else {
        const chosen = chooseApiTargetOrFallback("splash_screen", input.brief, spec);
        if (chosen.kind === "external") return chosen.plan;
        const apiModel = chosen.model;
        warnings.push(...chosen.warnings);
    
        const initialSeed = typeof spec.params["seed"] === "number" ? spec.params["seed"] : 0;
        const ck = computeCacheKey({
          model: apiModel,
          seed: initialSeed,
          prompt: spec.rewritten_prompt,
          params: spec.params
        });
        prompt_hash = ck.prompt_hash;
        params_hash = ck.params_hash;
        const gen = await generate(apiModel, {
          prompt: spec.rewritten_prompt,
          width: 1024,
          height: 1024,
          seed: initialSeed,
          ...(input.brand_bundle?.style_refs && { reference_images: input.brand_bundle.style_refs }),
          ...(input.brand_bundle?.palette && { palette: input.brand_bundle.palette }),
          ...(input.brand_bundle?.lora && { lora: input.brand_bundle.lora }),
          output_format: "png"
        });
        markPng = gen.image;
        modelUsed = gen.model;
        seed = gen.seed;
      }
    
      const platformSet = new Set(
        input.platforms.includes("all") ? ["ios", "android", "pwa"] : input.platforms
      );
    
      const variants: Array<{
        path: string;
        format: string;
        width?: number;
        height?: number;
        bytes?: number;
      }> = [];
    
      // Helper: composite mark centered at 40% of the canvas onto a flat background.
      async function composite(targetW: number, targetH: number): Promise<Buffer> {
        const markSize = Math.round(Math.min(targetW, targetH) * 0.4);
        const resizedMark = await sharp!(markPng)
          .resize(markSize, markSize, { fit: "contain", background: { r: 0, g: 0, b: 0, alpha: 0 } })
          .png()
          .toBuffer();
        return sharp!({
          create: { width: targetW, height: targetH, channels: 3, background }
        })
          .composite([
            {
              input: resizedMark,
              left: Math.round((targetW - markSize) / 2),
              top: Math.round((targetH - markSize) / 2)
            }
          ])
          .png()
          .toBuffer();
      }
    
      if (platformSet.has("ios")) {
        const iosDir = resolve(outDir, "ios");
        mkdirSync(iosDir, { recursive: true });
        const buf = await composite(2732, 2732);
        const p = resolve(iosDir, "LaunchScreen-2732.png");
        writeFileSync(p, buf);
        variants.push({ path: p, format: "png", width: 2732, height: 2732, bytes: buf.length });
        warnings.push(
          "iOS: LaunchScreen.storyboard is the modern way to do a launch screen. Use LaunchScreen-2732.png as the centered image in an Image View with backgroundColor = " +
            background +
            "."
        );
      }
    
      if (platformSet.has("android")) {
        const andDir = resolve(outDir, "android");
        mkdirSync(andDir, { recursive: true });
        const androidDpi: Array<{ dir: string; px: number }> = [
          { dir: "mipmap-mdpi", px: 192 },
          { dir: "mipmap-hdpi", px: 288 },
          { dir: "mipmap-xhdpi", px: 384 },
          { dir: "mipmap-xxhdpi", px: 576 },
          { dir: "mipmap-xxxhdpi", px: 768 }
        ];
        for (const d of androidDpi) {
          const dir = resolve(andDir, d.dir);
          mkdirSync(dir, { recursive: true });
          const buf = await composite(d.px, d.px);
          const p = resolve(dir, "splash.png");
          writeFileSync(p, buf);
          variants.push({ path: p, format: "png", width: d.px, height: d.px, bytes: buf.length });
        }
        const themeXml = `<?xml version="1.0" encoding="utf-8"?>
    <resources>
        <style name="Theme.App.Starting" parent="Theme.SplashScreen">
            <item name="windowSplashScreenBackground">${background}</item>
            <item name="windowSplashScreenAnimatedIcon">@mipmap/splash</item>
            <item name="postSplashScreenTheme">@style/Theme.App</item>
        </style>
    </resources>
    `;
        const themePath = resolve(andDir, "themes-splash.xml");
        writeFileSync(themePath, themeXml);
        variants.push({ path: themePath, format: "xml", bytes: Buffer.byteLength(themeXml) });
      }
    
      if (platformSet.has("pwa")) {
        const pwaDir = resolve(outDir, "pwa");
        mkdirSync(pwaDir, { recursive: true });
        const buf = await composite(1200, 1200);
        const p = resolve(pwaDir, "splash-1200.png");
        writeFileSync(p, buf);
        variants.push({ path: p, format: "png", width: 1200, height: 1200, bytes: buf.length });
      }
    
      const readme = `# Splash screens
    
    Generated by \`asset_generate_splash_screen\`.
    
    ## iOS
    
    \`ios/LaunchScreen-2732.png\` is a square centered mark for use inside a \`LaunchScreen.storyboard\`. In Xcode:
    
    1. Create a \`LaunchScreen.storyboard\` with a single \`UIImageView\` pinned to the safe area center.
    2. Set the background color of the view controller to \`${background}\`.
    3. Drop \`LaunchScreen-2732.png\` into \`Assets.xcassets\` and reference it from the image view.
    
    ## Android (API 31+)
    
    Android 12+ uses the system \`SplashScreen\` API. Wire in \`themes-splash.xml\` — set your app's launcher theme's parent to \`Theme.App.Starting\`.
    
    The \`mipmap-*dpi/splash.png\` files are drawables the system shows as the animated icon slot.
    
    ## PWA
    
    Add this to your \`manifest.webmanifest\`:
    
    \`\`\`json
    "splash_screens": [
      { "src": "/pwa/splash-1200.png", "sizes": "1200x1200", "type": "image/png" }
    ]
    \`\`\`
    
    iOS Safari still needs individual \`apple-touch-startup-image\` link tags per device; those are a legacy footgun we do NOT generate.
    `;
      const readmePath = resolve(outDir, "README.md");
      writeFileSync(readmePath, readme);
      variants.push({ path: readmePath, format: "md", bytes: Buffer.byteLength(readme) });
    
      const validation = await tier0({
        image: markPng,
        asset_type: "splash_screen",
        transparency_required: false,
        ...(input.brand_bundle && { brand_bundle: input.brand_bundle })
      });
    
      return {
        mode: "api",
        asset_type: "splash_screen",
        brief: input.brief,
        brand_bundle_hash: hashBundle(input.brand_bundle ?? null),
        variants,
        provenance: { model: modelUsed, seed, prompt_hash, params_hash },
        validations: validation,
        warnings
      };
    }
  • Zod schema `GenerateSplashScreenInput` defining input validation: brief (required), mode (inline_svg/external_prompt_only/api), brand_bundle, existing_mark_svg path, platforms list (ios/android/pwa/all), background_color hex, and output_dir.
    export const GenerateSplashScreenInput = z.object({
      brief: z.string().min(3),
      mode: ModeSchema.optional(),
      brand_bundle: BrandBundleSchema.optional(),
      existing_mark_svg: z
        .string()
        .optional()
        .describe(
          "Path to an existing brand mark SVG to center on the splash. If omitted, the brief is used to generate a mark first."
        ),
      platforms: z
        .array(z.enum(["ios", "android", "pwa", "all"]))
        .default(["all"])
        .describe(
          "Splash screens are platform-specific. iOS = LaunchScreen.storyboard hint + reference PNG at 2732×2732. Android = splash at 512×512 per mipmap (API 31+). PWA = 1200×1200 in manifest."
        ),
      background_color: z
        .string()
        .optional()
        .describe(
          "Splash background hex. Defaults to brand palette[0] or #ffffff. iOS uses this as the LaunchScreen backgroundColor; Android uses it as windowSplashScreenBackground."
        ),
      output_dir: z.string().optional()
    });
  • Tool registration in the TOOLS array with name 'asset_generate_splash_screen', description, inputSchema (JSON Schema version), and annotations.
    {
      name: "asset_generate_splash_screen",
      description:
        "Generate a cross-platform splash-screen bundle from a brand mark. Two modes (external_prompt_only / api); inline_svg is not supported (splash screens are PNG bundles — generate a logo inline_svg first, then call this with existing_mark_svg). api mode composites the mark onto background_color and emits ios/LaunchScreen-2732.png, android/mipmap-*dpi/splash.png, android/themes-splash.xml, pwa/splash-1200.png, and a README describing how to wire each.",
      inputSchema: {
        type: "object",
        properties: {
          brief: { type: "string" },
          mode: {
            type: "string",
            enum: ["external_prompt_only", "api"]
          },
          brand_bundle: { type: "object" },
          existing_mark_svg: {
            type: "string",
            description:
              "Path to an existing brand-mark SVG to center on the splash. Preferred over regenerating."
          },
          platforms: {
            type: "array",
            items: { type: "string", enum: ["ios", "android", "pwa", "all"] },
            default: ["all"]
          },
          background_color: { type: "string" },
          output_dir: { type: "string" }
        },
        required: ["brief"]
      },
      annotations: { openWorldHint: true }
    },
  • Router case statement that dispatches tool calls to `generateSplashScreen(GenerateSplashScreenInput.parse(args))` when the tool name is 'asset_generate_splash_screen'.
    case "asset_generate_splash_screen":
      result = await generateSplashScreen(GenerateSplashScreenInput.parse(args ?? {}));
      break;
  • Internal helper `composite()` that resizes the mark to 40% of the target canvas and composites it centered on a solid background color using Sharp.
    async function composite(targetW: number, targetH: number): Promise<Buffer> {
      const markSize = Math.round(Math.min(targetW, targetH) * 0.4);
      const resizedMark = await sharp!(markPng)
        .resize(markSize, markSize, { fit: "contain", background: { r: 0, g: 0, b: 0, alpha: 0 } })
        .png()
        .toBuffer();
      return sharp!({
        create: { width: targetW, height: targetH, channels: 3, background }
      })
        .composite([
          {
            input: resizedMark,
            left: Math.round((targetW - markSize) / 2),
            top: Math.round((targetH - markSize) / 2)
          }
        ])
        .png()
        .toBuffer();
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description lists output files and states that inline_svg is not supported. However, with only openWorldHint: true in annotations, it does not specify behaviors like file overwriting, permissions, or network calls. The added detail is moderate.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise (two sentences), front-loads the purpose, and efficiently delivers constraints and output details without redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (7 parameters, nested objects, multiple platforms, multiple output files), the description covers the core workflow, key constraints, and output file list. It lacks details on some parameters but is largely complete for a generation tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is only 14%; the description adds meaning to existing_mark_svg (preferred path) and explains mode values. It does not elaborate on brief, brand_bundle, or output_dir, leaving gaps in understanding.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: generating a cross-platform splash-screen bundle from a brand mark. It specifies that inline_svg is not supported and outlines the output files, distinguishing it from sibling tools like asset_generate_app_icon.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains the prerequisite: first generate a logo inline_svg then call this tool with existing_mark_svg. It also mentions two modes but does not elaborate on when to use each. It provides clear context for use.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/MohamedAbdallah-14/prompt-to-asset'

If you have feedback or need assistance with the MCP directory API, please join our Discord server