Skip to content

jillmpla/fancyalt

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

16 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

✨ FancyAlt

FancyAlt is an accessibility-focused web application and API that uses OpenAI to turn images into clear, useful text.

Users can upload an image or provide a public image URL, choose one result type, and receive that requested output: concise alt text, a detailed description, a social media description, a short creative story, or an image safety check.

🌐 Live Web App: https://fancyalt.com

πŸ“š Interactive API Documentation: https://fancyalt.com/api-docs


πŸ“Έ What FancyAlt Can Do

  • Analyze uploaded JPEG, PNG, and WEBP images
  • Analyze publicly accessible HTTP and HTTPS image URLs
  • Generate concise, accessibility-focused alt text
  • Generate detailed visual descriptions
  • Create natural descriptions for social media
  • Create short fictional stories inspired by an image
  • Extract meaningful readable text when requested
  • Moderate every image before returning generated content
  • Return consistent, structured JSON results
  • Provide a responsive, keyboard-accessible interface
  • Support light and dark themes
  • Provide interactive Swagger API documentation

πŸ’‘ Mode-Specific Request Design

FancyAlt requests only the result type selected by the user.

Selected Mode Generated Result
concise Concise alt text
detailed Detailed visual description
social Social media description
story Short fictional story
moderateOnly Moderation result only

Every image is still checked by the moderation service first.

When visible-text extraction is enabled, the text-detection fields are included in the same mode-specific image request. FancyAlt does not make a second image request for visible text.

The OpenAI client uses maxRetries: 0, so a failed mode-specific image request is not silently repeated by the SDK.


πŸ›  Tech Stack

  • Backend: Node.js 20+ and Express.js 5
  • AI Image Processing: OpenAI Responses API
  • Image and Story Model: gpt-5.4-mini
  • Content Moderation: omni-moderation-latest
  • Structured Output: Zod
  • Image Uploads: Multer memory storage
  • Request Validation: express-validator
  • Frontend: HTML5, custom CSS, and vanilla JavaScript
  • UI Assets: Bootstrap, Font Awesome, and JSONEditor
  • Documentation: OpenAPI 3.0, Swagger UI, and YAMLJS
  • Security: Helmet, CORS, rate limiting, and custom input sanitization

πŸ” How It Works

Uploaded Images

  1. The user uploads a JPEG, PNG, or WEBP image.
  2. Multer validates the file type and 5 MB size limit.
  3. The image is held temporarily in memory.
  4. OpenAI Moderation checks the image.
  5. If the mode is moderateOnly, FancyAlt returns the moderation result immediately.
  6. Otherwise, FancyAlt makes one mode-specific image request and returns only the selected output.

Public Image URLs

  1. The user submits a public HTTP or HTTPS image URL.
  2. FancyAlt validates the URL and rejects localhost or loopback addresses.
  3. OpenAI Moderation checks the image.
  4. If the mode is moderateOnly, FancyAlt returns the moderation result immediately.
  5. Otherwise, FancyAlt makes one mode-specific image request and returns only the selected output.

πŸ“¦ Result Structure

A successful non-moderation response includes:

  • Mode: The selected result type
  • Moderation: Safety results for the image
  • Output Text: Only the requested description or story
  • Model: The OpenAI model used for the generated output
  • Visible Text: Optionally returned only when visible-text extraction is enabled

πŸ§ͺ Analysis Modes

Mode Purpose
concise Creates focused alt text for websites, documents, and applications
detailed Creates a fuller visual description
social Creates a natural description for social media
story Creates a short fictional story directly from the image
moderateOnly Runs only the image safety check

βš™οΈ Installation

1. Clone the Repository

git clone https://github.com/jillmpla/fancyalt.git
cd fancyalt

2. Check Node.js

FancyAlt requires Node.js 20 or newer.

node --version

3. Install Dependencies

npm install

4. Create the Environment File

Copy .env.example to a new file named .env.

The environment file uses these values:

OPENAI_API_KEY=your-openai-api-key

OPENAI_IMAGE_MODEL=gpt-5.4-mini
OPENAI_STORY_MODEL=gpt-5.4-mini
OPENAI_MODERATION_MODEL=omni-moderation-latest

Replace the placeholder API key in .env with your own OpenAI API key.

5. Start the Application

npm start

For development:

npm run dev

6. Open the Application


πŸ“¦ API Overview

Base URLs

Production: https://fancyalt.com/api
Local:      http://localhost:5000/api

Endpoints

Method Endpoint Purpose
POST /api/generate-caption Analyze an uploaded image
POST /api/analyze-url Analyze a public image URL
GET /api/status Check whether the API is running

πŸ–ΌοΈ Upload an Image

Endpoint

POST /api/generate-caption

Content Type

multipart/form-data

Request Fields

Field Type Required Description
image File Yes JPEG, PNG, or WEBP image up to 5 MB
mode String No Result mode; defaults to concise
maxLength Integer No Preferred maximum length from 40 to 1,000 characters
includeVisibleText Boolean No Whether readable text should be extracted; defaults to true when omitted

cURL Example

curl -X POST http://localhost:5000/api/generate-caption \
  -F "image=@example.jpg" \
  -F "mode=concise" \
  -F "maxLength=160" \
  -F "includeVisibleText=true"

πŸ”— Analyze a Public Image URL

Endpoint

POST /api/analyze-url

Content Type

application/json

Example Request

{
  "imageUrl": "https://example.com/images/kayak-dog.jpg",
  "mode": "detailed",
  "maxLength": 500,
  "includeVisibleText": true
}

cURL Example

curl -X POST http://localhost:5000/api/analyze-url \
  -H "Content-Type: application/json" \
  -d '{
    "imageUrl": "https://example.com/images/kayak-dog.jpg",
    "mode": "detailed",
    "maxLength": 500,
    "includeVisibleText": true
  }'

The URL must be publicly accessible without authentication and must use HTTP or HTTPS.


πŸ“Š Example Responses

Concise Alt Text

{
  "mode": "concise",
  "flagged": false,
  "moderation": {
    "flagged": false,
    "flaggedCategories": [],
    "categories": {
      "sexual": false,
      "violence": false,
      "self-harm": false
    },
    "categoryScores": {
      "sexual": 0.00012,
      "violence": 0.00048,
      "self-harm": 0.00001
    },
    "appliedInputTypes": {
      "sexual": ["image"],
      "violence": ["image"]
    },
    "model": "omni-moderation-latest"
  },
  "output": {
    "text": "Golden retriever sitting beside a blue kayak at the edge of a calm lake.",
    "model": "gpt-5.4-mini",
    "containsText": false,
    "visibleText": []
  }
}

The containsText and visibleText properties are omitted when includeVisibleText is false.

Story Mode

{
  "mode": "story",
  "flagged": false,
  "moderation": {
    "flagged": false,
    "flaggedCategories": [],
    "categories": {},
    "categoryScores": {},
    "appliedInputTypes": {},
    "model": "omni-moderation-latest"
  },
  "output": {
    "text": "The retriever waited beside the kayak as the lake turned gold in the evening light. One quiet paddle remained before home.",
    "model": "gpt-5.4-mini"
  }
}

Story mode creates the story directly from the image. It doesn't first request a separate alt-text analysis.

Moderation-Only Mode

{
  "mode": "moderateOnly",
  "flagged": false,
  "moderation": {
    "flagged": false,
    "flaggedCategories": [],
    "categories": {},
    "categoryScores": {},
    "appliedInputTypes": {},
    "model": "omni-moderation-latest"
  }
}

Moderation-only responses don't include an output property.


❀️ Status Endpoint

GET /api/status

The status endpoint returns service, version, environment, uptime, provider, and timestamp information.


🚨 Error Handling

FancyAlt uses centralized error handling, consistent status codes, and request IDs.

Example Validation Error

{
  "error": "Validation failed.",
  "requestId": "1c8ca594-8758-4c97-bb86-49cff66704f8",
  "details": [
    {
      "field": "mode",
      "location": "body",
      "message": "Mode must be one of the supported values.",
      "value": "invalidMode"
    }
  ]
}

Common Status Codes

Code Meaning
200 Request completed successfully
400 Request data is missing or invalid
403 Request origin or resource is not allowed
404 Route does not exist
413 Uploaded image exceeds the 5 MB limit
415 Uploaded file type is not supported
422 Image could not be retrieved or processed
429 FancyAlt's API rate limit was exceeded
500 Unexpected server error
502 An upstream service returned an invalid response
503 OpenAI authentication, rate limiting, or service availability prevented processing

Temporary 503 responses may include a Retry-After header.

Every request receives an X-Request-ID response header. Error responses also include the request ID in the JSON body.


πŸ” Security and Privacy

  • The OpenAI API key is stored only on the server.
  • Uploaded images are held in memory and are not intentionally saved to disk.
  • Only one image is accepted per request.
  • Uploads are limited to 5 MB.
  • Only JPEG, PNG, and WEBP uploads are accepted.
  • Public image URLs are validated before processing.
  • Localhost and loopback image URLs are rejected.
  • Custom middleware removes dangerous request keys.
  • Helmet applies standard HTTP security headers.
  • CORS limits browser requests to approved origins.
  • Each IP address is limited to 25 API requests every 15 minutes.
  • Every request receives a unique request ID.

🀝 Frontend Accessibility

The FancyAlt interface includes:

  • Semantic HTML landmarks
  • Clear form labels and descriptions
  • A skip-navigation link
  • Keyboard-accessible source tabs
  • Keyboard-accessible upload controls
  • Visible keyboard focus indicators
  • Screen-reader status announcements
  • Light and dark themes
  • Reduced-motion support
  • Human-readable result cards
  • Copy-to-clipboard controls
  • Responsive layouts

πŸ—‚οΈ Project Structure

fancyalt/
β”œβ”€β”€ .env.example
β”œβ”€β”€ .gitignore
β”œβ”€β”€ index.js
β”œβ”€β”€ package.json
β”œβ”€β”€ package-lock.json
β”œβ”€β”€ README.md
β”œβ”€β”€ swagger.yaml
β”œβ”€β”€ LICENSE.txt
β”‚
β”œβ”€β”€ middlewares/
β”‚   β”œβ”€β”€ asyncHandler.js
β”‚   β”œβ”€β”€ sanitizeKeys.js
β”‚   └── validate.js
β”‚
β”œβ”€β”€ routes/
β”‚   β”œβ”€β”€ caption.js
β”‚   └── status.js
β”‚
β”œβ”€β”€ services/
β”‚   β”œβ”€β”€ imageAnalyzer.js
β”‚   β”œβ”€β”€ imageInput.js
β”‚   β”œβ”€β”€ imageModerator.js
β”‚   β”œβ”€β”€ openaiClient.js
β”‚   └── storyGenerator.js
β”‚
β”œβ”€β”€ utils/
β”‚   └── errors.js
β”‚
└── public/
    β”œβ”€β”€ index.html
    β”œβ”€β”€ index.css
    β”œβ”€β”€ footer.css
    β”œβ”€β”€ footer.js
    β”œβ”€β”€ swagger-custom.css
    β”‚
    └── js/
        └── app.js

Static images, Bootstrap files, Font Awesome files, and third-party vendor assets are omitted for readability.


πŸ“š API Documentation

FancyAlt includes interactive Swagger documentation generated from swagger.yaml.


πŸ“„ License

See LICENSE.txt for the full license terms.


πŸ“¬ Contact

For questions or support, email fancyaltdotcom@gmail.com.


⭐ Support the Project

If you find FancyAlt useful, consider giving the repository a star.

About

FancyAlt is a full-stack, accessibility-focused image analysis app and API built with Node.js, Express, and OpenAI. It supports image uploads and public URLs to generate alt text, detailed descriptions, visible text, moderation results, and short creative stories.

Topics

Resources

License

Stars

2 stars

Watchers

2 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors