S3S3eidat Tools MCP

All tools

162 tools in 10 groups. Every one runs on your machine. Click a tool for its inputs and an example.

PDF

Merge, split, organise, compress, watermark, number, sign and fill PDFs; text and images out.

pdf_compressCompress PDF

Shrink a PDF by re-rendering every page as a JPEG image (best for scans and photo-heavy files). Presets like the website: email (96 DPI, quality 55), balanced (144 DPI, 72), high (220 DPI, 85); or set dpi/quality yourself; optional grayscale. The result loses selectable text, links and form fields. Reports before/after size and a verdict ("bigger" means keep the original: the PDF was already efficient). Web version: https://s3eidat.github.io/pdf-compress/

InputTypeDescription
file requiredstringThe PDF: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
preset"email" | "balanced" | "high"
default "balanced"
Quality preset. Default "balanced".
dpiintegerOverride the preset resolution (36-300 DPI).
qualityintegerOverride the preset JPEG quality (30-95).
grayscaleboolean
default false
Convert pages to grayscale.
passwordstringPassword, if the PDF is protected.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example: small enough to attach to an email

{
  "file": "scan.pdf",
  "preset": "email"
}
pdf_extract_pagesExtract PDF pages

Make one new PDF from selected pages of a PDF, e.g. "1-3, 7, 10-" (order as written, each page once). The rest of the file is left out. To reorder, duplicate or delete pages use pdf_organize. Web version: https://s3eidat.github.io/pdf-split/

InputTypeDescription
file requiredstringThe PDF: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
pages requiredstringPages to keep: "1-3, 7, 10-" ("5-" = to the end; also "odd", "even", "last").
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example: writes report-pages-1-3_7.pdf

{
  "file": "report.pdf",
  "pages": "1-3, 7"
}
pdf_form_fieldsList PDF form fields

List the fillable fields of a PDF form in reading order: name, readable label, type (text, checkbox, radio, dropdown, list, signature, button), page, current value, options, and whether it is required / read-only / multi-line. Use the names with pdf_form_fill. Web version: https://s3eidat.github.io/pdf-fill/

InputTypeDescription
file requiredstringThe PDF: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).

Example

{
  "file": "application.pdf"
}
pdf_form_fillFill a PDF form

Fill PDF form fields by name (get names from pdf_form_fields): text as strings, checkboxes as true/false, radio groups and dropdowns by option text, lists as arrays. Optionally flatten (bake the answers into the page, no longer editable). Only changed fields are redrawn. Text the standard font cannot draw (Arabic…) is kept and viewers are asked to draw it, but cannot be flattened. Web version: https://s3eidat.github.io/pdf-fill/

InputTypeDescription
file requiredstringThe PDF: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
values requiredobjectField name -> value, e.g. {"name": "Sara", "agree": true, "plan": "pro", "langs": ["Arabic"]}.
flattenboolean
default false
Flatten the form after filling (fields become plain page content).
ignoreUnknownboolean
default false
Skip names that are not in the form instead of failing.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example

{
  "file": "application.pdf",
  "values": {
    "name": "Sara Haddad",
    "agree": true,
    "plan": "pro",
    "country": "Egypt"
  },
  "flatten": true
}
pdf_from_imagesImages to PDF

Make a PDF from images (JPEG, PNG, WebP, GIF, TIFF, AVIF, SVG…), one image per page, in the order given. Page size A4, Letter, or "fit" (page = image size at 96 dpi); orientation auto/portrait/landscape; margin in mm; quality "original" (JPEG/PNG kept as-is), high (≤3000 px, JPEG 90), medium (≤2000 px, 75) or small (≤1400 px, 60). Photos are turned upright from their EXIF orientation. Web version: https://s3eidat.github.io/images-to-pdf/

InputTypeDescription
images required(string | object)[]Images in page order: path/base64 strings, or {file, rotate}.
pageSize"a4" | "letter" | "fit"
default "a4"
Page size. Default "a4".
orientation"auto" | "portrait" | "landscape"
default "auto"
"auto" turns the page to match each image. Ignored for "fit".
marginnumber
default 10
Margin around each image in mm. Default 10.
quality"original" | "high" | "medium" | "small"
default "high"
Image quality / size trade-off. Default "high".
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example

{
  "images": [
    "receipt1.jpg",
    "receipt2.jpg"
  ],
  "output": "receipts.pdf"
}

Example

{
  "images": [
    "scan.png",
    {
      "file": "photo.jpg",
      "rotate": 90
    }
  ],
  "pageSize": "fit",
  "margin": 0,
  "quality": "original"
}
pdf_infoPDF info

Quick facts about a PDF for planning the next step: page count, page sizes (points, mm, paper name such as "A4 portrait", grouped by size), rotated pages, encryption, PDF version, form field count, title/author, and which pages have no text layer (scans that would need OCR). Related web tool: https://s3eidat.github.io/pdf-metadata/

InputTypeDescription
file requiredstringThe PDF: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
passwordstringPassword, for the text check on a protected PDF.
textCheckboolean
default true
Check which pages have a text layer (reads up to 500 pages). Default true.

Example: { pages: 12, sizes: [{ paper: "A4 portrait", pages: "1-12" }], pagesWithoutText: [1, …] }

{
  "file": "scan.pdf"
}
pdf_mergeMerge PDFs

Combine several PDFs into one, in the order given. Each entry can be a file (path or base64) or {file, pages} to take only some pages ("1-3, 5, 8-"; "3-1" keeps reverse order; a page can repeat). Password-protected PDFs must be unlocked first. Web version: https://s3eidat.github.io/pdf-merge/

InputTypeDescription
files required(string | object)[]PDFs to merge, in order: a path/base64 string, or {file, pages}.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example: report + pages 2-4 of the appendix

{
  "files": [
    "report.pdf",
    {
      "file": "appendix.pdf",
      "pages": "2-4"
    }
  ],
  "output": "merged.pdf"
}
pdf_metadata_readRead PDF metadata

Show what a PDF reveals about itself: Title, Author, Subject, Keywords, Creator, Producer, creation/modification dates (with time zone), custom info fields, XMP metadata (size, edit history, creator) and the document ID. Optionally returns the XMP XML. Web version: https://s3eidat.github.io/pdf-metadata/

InputTypeDescription
file requiredstringThe PDF: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
includeXmpboolean
default false
Also return the decoded XMP XML.

Example

{
  "file": "contract.pdf"
}
pdf_metadata_writeEdit or strip PDF metadata

Change PDF info fields (title, author, subject, keywords, creator, producer, dates, custom fields) or strip all metadata: info fields, XMP, page-level data and the document ID, and drop unreachable leftovers from earlier edits so no trace stays in the bytes. Untouched values keep their exact original form. Web version: https://s3eidat.github.io/pdf-metadata/

InputTypeDescription
file requiredstringThe PDF: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
stripboolean
default false
Remove ALL metadata (other edits are ignored).
titlestringTitle. "" removes it; omit to keep it.
authorstringAuthor. "" removes it; omit to keep it.
subjectstringSubject. "" removes it; omit to keep it.
keywordsstringKeywords. "" removes it; omit to keep it.
creatorstringCreator (the app that made it). "" removes it; omit to keep it.
producerstringProducer (the PDF engine). "" removes it; omit to keep it.
creationDatestringCreation date as ISO 8601, e.g. "2026-03-14T09:30:00+03:00". "" removes it; omit to keep it.
modDatestringModification date as ISO 8601. "" removes it; omit to keep it.
customobjectCustom info fields to set; null removes one. E.g. {"Company": null}.
removeXmpboolean
default false
Remove the XMP metadata stream (and page-level XMP/PieceInfo).
removeDocumentIdboolean
default false
Remove the document ID from the trailer.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example

{
  "file": "report.pdf",
  "title": "Final report",
  "author": "Sara Haddad"
}

Example: writes cv-clean.pdf with no metadata

{
  "file": "cv.pdf",
  "strip": true
}
pdf_organizeOrganize PDF pages

Rebuild a PDF from a page list: reorder, delete (leave pages out), duplicate (list a page twice) and insert blank pages. "3, 1-2, blank, 5-" puts page 3 first, then 1-2, a blank page (same size as the page before it), then 5 to the end; page 4 is dropped. Optionally rotate pages of the result. Existing page rotation is kept. Web version: https://s3eidat.github.io/pdf-organizer/

InputTypeDescription
file requiredstringThe PDF: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
pages requiredstringNew page order (1-based source pages): e.g. "3, 1-2, blank, 2, 5-". Pages not listed are removed; "blank" adds an empty page.
rotateobject[]Optional rotations applied to pages of the new order.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example: swap the first two pages, turn page 3

{
  "file": "scan.pdf",
  "pages": "2, 1, 3-",
  "rotate": [
    {
      "pages": "3",
      "angle": 90
    }
  ]
}
pdf_page_numbersAdd page numbers

Add page numbers to a PDF, plus an optional header and footer line. Format tokens: {n}, {total}, {date}, {filename} (e.g. "Page {n} of {total}", "{n} / {total}", "- {n} -"). Choose the corner/edge, start number, how many leading pages to skip (e.g. a cover), a page range, font, size, colour and margin. Numbers stay upright on rotated pages. Web version: https://s3eidat.github.io/pdf-page-numbers/

InputTypeDescription
file requiredstringThe PDF: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
formatstring
default "Page {n} of {total}"
Number format with {n}, {total}, {date}, {filename}. Default "Page {n} of {total}". Empty = no number (header/footer only).
position"tl" | "tc" | "tr" | "bl" | "bc" | "br"
default "bc"
Where the number goes: t/b = top/bottom, l/c/r = left/centre/right. Default "bc".
startinteger
default 1
Number of the first numbered page. Default 1.
skipinteger
default 0
Leave this many first pages without a number (e.g. 1 for a cover). Default 0.
pagesstringPages to number: 1-based list like "1-3, 5, 8-" ("5-" = to the end, "-3" = first three, "3-1" = reversed). Also "all", "odd", "even", "last". Empty = all pages.
font"Helvetica" | "HelveticaBold" | "TimesRoman" | "Courier"
default "Helvetica"
sizenumber
default 10
Text size in points. Default 10.
colorstring
default "#333333"
Colour as hex "#rrggbb". Default #333333.
marginnumber
default 28
Distance from the page edge in points. Default 28.
headerstring
default ""
Optional header text on numbered pages (tokens allowed).
headerAlign"l" | "c" | "r"
default "c"
Header alignment: l, c or r.
footerstring
default ""
Optional footer text on numbered pages (tokens allowed).
footerAlign"l" | "c" | "r"
default "c"
Footer alignment: l, c or r.
datestringText for {date}. Default: today in the server locale.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example: no number on the cover

{
  "file": "report.pdf",
  "format": "Page {n} of {total}",
  "position": "br",
  "skip": 1
}

Example

{
  "file": "thesis.pdf",
  "format": "{n}",
  "footer": "Draft, {date}",
  "footerAlign": "l"
}
pdf_rotateRotate PDF pages

Rotate selected pages of a PDF by 90, 180 or 270 degrees clockwise (-90 = counter-clockwise), on top of any rotation they already have. Other pages, links and form fields are left as they are. Web version: https://s3eidat.github.io/pdf-organizer/

InputTypeDescription
file requiredstringThe PDF: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
angle required90 | 180 | 270 | -90Clockwise degrees: 90, 180, 270, or -90.
pagesstringPages to rotate: 1-based list like "1-3, 5, 8-" ("5-" = to the end, "-3" = first three, "3-1" = reversed). Also "all", "odd", "even", "last". Empty = all pages.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example

{
  "file": "scan.pdf",
  "angle": 90,
  "pages": "even"
}
pdf_sign_imageSign PDF with an image

Place a signature (or initials, stamp, logo) image on PDF pages at x/y/width in points, measured from the top-left of the page as it is shown (rotated pages are handled). A light paper background is removed by default so scanned signatures sit cleanly on the page. Optional text marks (name, date…) can be added too. This is a visual signature, not a cryptographic digital signature. To draw a signature by hand, use the web version: https://s3eidat.github.io/pdf-sign/

InputTypeDescription
file requiredstringThe PDF: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
signaturestringSignature image: local path or base64 (PNG, JPEG, WebP…).
pagesstring
default "last"
Pages to sign: "last" (default), "1", "all", "1-3"…
xnumber
default 72
Left edge in points from the left of the page (72 pt = 1 inch). Default 72.
ynumberTop edge in points from the TOP of the page. Default: 72 pt above the bottom margin.
widthnumber
default 150
Width in points. Default 150 (about 5.3 cm).
heightnumberHeight in points. Default: keeps the image aspect ratio.
removeBackgroundboolean
default true
Make light (paper) pixels transparent and trim to the ink. Default true.
thresholdinteger
default 210
Brightness (0-255) above which pixels become transparent. Default 210.
textsobject[]Optional text marks, e.g. the printed name or the date.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example

{
  "file": "contract.pdf",
  "signature": "signature.png",
  "pages": "last",
  "x": 360,
  "y": 700,
  "width": 160
}

Example

{
  "file": "form.pdf",
  "signature": "sig.jpg",
  "pages": "2",
  "x": 80,
  "y": 620,
  "texts": [
    {
      "text": "29/10/2026",
      "page": 2,
      "x": 300,
      "y": 640
    }
  ]
}
pdf_splitSplit PDF

Split one PDF into several PDFs written to a folder: by page ranges (one file per range, e.g. "1-3, 4-6, 7-"), every N pages, or one file per page. Files are named like the website: <name>-pages-1-3.pdf, <name>-part-01.pdf, <name>-page-001.pdf. Use pdf_extract_pages to get a single PDF with chosen pages. Web version: https://s3eidat.github.io/pdf-split/

InputTypeDescription
file requiredstringThe PDF: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
mode required"ranges" | "every" | "single""ranges": one PDF per range in `ranges`; "every": a new PDF every `every` pages; "single": one PDF per page.
rangesstringFor mode "ranges": comma-separated ranges, each becomes one PDF, e.g. "1-3, 4-6, 7-".
everyintegerFor mode "every": pages per file (e.g. 2 makes 1-2, 3-4…).
outputstringFolder to write the PDFs into (created if missing). Default: a new folder in the server output folder.

Example: three PDFs

{
  "file": "book.pdf",
  "mode": "ranges",
  "ranges": "1-10, 11-20, 21-"
}

Example

{
  "file": "scans.pdf",
  "mode": "every",
  "every": 2
}
pdf_to_imagesPDF to images

Render PDF pages to PNG, JPEG or WebP images in a folder (one file per page, named <name>-p001.png…). Choose the DPI (72 = screen size, 150 = default, 300 = print), JPEG/WebP quality, a page range, and a transparent background for PNG/WebP. Very large pages are capped at 16384 px a side / 16.7 megapixels, like the website. Web version: https://s3eidat.github.io/pdf-to-images/

InputTypeDescription
file requiredstringThe PDF: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
format"png" | "jpeg" | "webp"
default "png"
Image format. Default "png".
dpinumber
default 150
Resolution in dots per inch. Default 150.
qualityinteger
default 90
JPEG/WebP quality 1-100. Default 90.
transparentboolean
default false
Keep the page background transparent (PNG/WebP only).
pagesstringPages to render: 1-based list like "1-3, 5, 8-" ("5-" = to the end, "-3" = first three, "3-1" = reversed). Also "all", "odd", "even", "last". Empty = all pages.
passwordstringPassword, if the PDF is protected.
outputstringFolder to write the images into (created if missing). Default: a new folder in the server output folder.

Example

{
  "file": "slides.pdf",
  "format": "jpeg",
  "dpi": 96,
  "pages": "1-5"
}

Example

{
  "file": "logo.pdf",
  "format": "png",
  "dpi": 300,
  "transparent": true
}
pdf_to_textPDF to text

Extract the text of a PDF with lines and paragraphs rebuilt (right-to-left Arabic/Hebrew lines in reading order), as plain text or Markdown, with optional page separators and reflow (join lines, mend hyphenated words). Returns per-page word counts and flags pages with no text layer (scans: this tool does no OCR). Supports page ranges and passwords. Give "output" to write a .txt/.md file instead of returning the text inline. Web version: https://s3eidat.github.io/pdf-to-text/

InputTypeDescription
file requiredstringThe PDF: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
pagesstringPages to read: 1-based list like "1-3, 5, 8-" ("5-" = to the end, "-3" = first three, "3-1" = reversed). Also "all", "odd", "even", "last". Empty = all pages.
passwordstringPassword, if the PDF is protected.
format"text" | "markdown"
default "text"
"text" or "markdown" (## Page N headings, reflowed paragraphs).
separatorsboolean
default true
Put a "——— Page N ———" line before each page. Default true.
reflowboolean
default false
Join the lines of each paragraph and mend words split by a hyphen.
perPageboolean
default false
Also return each page's text separately.
maxCharsinteger
default 200000
Cut the inline text after this many characters (the file output is never cut).
outputstringWrite the text to this file (or folder ending in "/") instead of returning it inline.

Example

{
  "file": "article.pdf",
  "reflow": true
}

Example

{
  "file": "book.pdf",
  "pages": "1-20",
  "format": "markdown",
  "output": "book.md"
}
pdf_watermarkWatermark PDF

Stamp a text or image watermark on PDF pages: centred or tiled across the page, with opacity, angle, size, colour and a page range. It reads upright on rotated pages. Text the standard PDF fonts cannot draw (Arabic, CJK…) is stamped as an image. Defaults match the website: "CONFIDENTIAL", Helvetica Bold 72 pt, #d64545, 25% opacity, 45°. Web version: https://s3eidat.github.io/pdf-watermark/

InputTypeDescription
file requiredstringThe PDF: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
textstringWatermark text (default "CONFIDENTIAL"). Ignored when "image" is given.
imagestringImage watermark instead of text: a local path or base64 (PNG, JPEG, WebP, SVG…).
font"HelveticaBold" | "Helvetica" | "TimesRomanBold" | "CourierBold"
default "HelveticaBold"
Font for text.
sizenumber
default 72
Text size in points. Default 72.
colorstring
default "#d64545"
Colour as hex "#rrggbb". Default #d64545.
scalenumber
default 40
Image width as % of the page width. Default 40.
opacitynumber
default 25
Opacity in % (1-100). Default 25.
anglenumber
default 45
Rotation in degrees, counter-clockwise as shown. Default 45.
mode"center" | "tile"
default "center"
"center": one stamp in the middle; "tile": repeated across the page.
gapnumber
default 80
Space between tiles in points (tile mode). Default 80.
pagesstringPages to stamp: 1-based list like "1-3, 5, 8-" ("5-" = to the end, "-3" = first three, "3-1" = reversed). Also "all", "odd", "even", "last". Empty = all pages.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example

{
  "file": "roadmap.pdf",
  "text": "DRAFT",
  "opacity": 20
}

Example

{
  "file": "brochure.pdf",
  "image": "logo.png",
  "mode": "tile",
  "scale": 15,
  "angle": 0,
  "opacity": 10
}

Images

Convert, resize, crop, compress, watermark, EXIF, palettes, favicons, placeholders.

image_ascii_artImage to ASCII art

Turn an image into ASCII art: each character cell takes the average brightness of its area (cells are twice as tall as wide) and maps it onto a character ramp: standard " .:-=+*#%@", detailed (70 characters), blocks " ░▒▓█", simple, binary, or your own. Contrast, brightness and invert (for light text on dark backgrounds). Returns the text; format "png" also renders it to an image (optionally in the image's colours). Transparent areas count as white paper. Web version: https://s3eidat.github.io/ascii-art/

InputTypeDescription
image requiredstringThe image: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
columnsinteger
default 100
Characters per line. Default 100.
ramp"standard" | "detailed" | "blocks" | "simple" | "binary" | "custom"
default "standard"
Character set, light → dark. Default standard.
customRampstring
default " .oO0@"
Characters for ramp "custom", from no ink to most ink (at least 2).
contrastnumber
default 0
-100 … 100. Default 0.
brightnessnumber
default 0
-100 … 100. Default 0.
invertboolean
default false
For light text on a dark background. Default false.
format"text" | "png"
default "text"
text (default) or png (also writes <name>-ascii.png).
colorboolean
default false
PNG only: colour each character with the image colour.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).

Example

{
  "image": "/photos/cat.jpg",
  "columns": 80,
  "contrast": 20
}

Example

{
  "image": "/photos/logo.png",
  "ramp": "blocks",
  "format": "png",
  "color": true
}
image_ascii_bannerText to ASCII banner

Big banner letters from text, using the website's own bitmap fonts: block (█ 5×7), char (any fill character, e.g. #), shadow (█ with ░ shade), compact (half-block 5×7), small (█ 3×5), tiny (half-block 3×5). Letters A-Z, digits and common punctuation (unknown characters show as "?"); one banner per input line. Returns the text; format "png" also renders it to banner.png. Web version: https://s3eidat.github.io/ascii-art/

InputTypeDescription
text requiredstringText to render (case-insensitive). New lines make separate banners.
style"block" | "char" | "shadow" | "compact" | "small" | "tiny"
default "block"
Letter style. Default block.
fillstring
default "#"
Fill character(s) for style "char". Default "#".
format"text" | "png"
default "text"
text (default) or png (also writes banner.png).
theme"dark" | "light"
default "dark"
PNG colours: dark (teal on near-black, default) or light.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).

Example: A 7-line banner: "## ## ######" …

{
  "text": "HI",
  "style": "char",
  "fill": "#"
}

Result

{
  "lines": 7
}
image_compressCompress image

Make an image file smaller: re-encode at a quality (default 80) and optionally cap the longest side (maxSide), as JPEG, WebP, AVIF or PNG ("keep" = same format; GIF/BMP/TIFF become PNG). Or give targetBytes to find the highest quality that fits that size. With keepSmaller (default on) the original is kept when re-encoding would not make it smaller. Reports before/after sizes and % saved. Transparent areas become white in JPEG. Metadata (EXIF, GPS) is not copied. PNG is lossless (quality ignored). Web version: https://s3eidat.github.io/image-squeeze/

InputTypeDescription
image requiredstringThe image: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
format"keep" | "jpeg" | "webp" | "avif" | "png"
default "keep"
Output format. Default keep (same as the input).
qualityinteger
default 80
Quality 1-100 for JPEG / WebP / AVIF. Default 80 (website default).
maxSideintegerScale down so the longest side is at most this many pixels (e.g. 1920).
targetBytesintegerAim for a file of at most this many bytes: searches the highest quality that fits (lossy formats only; overrides quality).
keepSmallerboolean
default true
Keep the original bytes when the result would be larger (same format and size only). Default true.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example

{
  "image": "/photos/IMG_0042.jpg",
  "quality": 75,
  "maxSide": 1920
}

Example: Highest WebP quality that fits 150 KB.

{
  "image": "/site/hero.png",
  "format": "webp",
  "targetBytes": 150000
}
image_convertConvert image format

Convert an image to PNG, JPEG, WebP, AVIF, GIF or TIFF. Reads JPEG, PNG, WebP, AVIF/HEIF, GIF (first frame), TIFF, SVG, BMP and ICO. Transparent areas are filled with `background` when writing JPEG (or always with `flatten`). EXIF rotation is applied. SVGs are rasterised at their own size (width/height or viewBox; 1024×1024 if neither), scripts never run. Images larger than 16384 px a side or 100 MP are scaled down to fit. Web version: https://s3eidat.github.io/image-convert/

InputTypeDescription
image requiredstringThe image: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
format"png" | "jpeg" | "webp" | "avif" | "gif" | "tiff"
default "png"
Output format. Default png (as on the website).
qualityinteger
default 92
Quality 1-100 for JPEG / WebP / AVIF (ignored for PNG). Default 92, same as the website.
backgroundstring
default "#ffffff"
Colour used for transparent areas when writing JPEG or when flatten is on. Default #ffffff.
flattenboolean
default false
Fill transparency with the background for every format, not only JPEG.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example: Writes logo.jpg with transparency on white.

{
  "image": "/photos/logo.png",
  "format": "jpeg",
  "quality": 90,
  "background": "#ffffff"
}

Example

{
  "image": "/icons/icon.svg",
  "format": "webp"
}
image_cropCrop image

Crop an image to a pixel box (x, y, width, height) or to an aspect ratio (1:1, 4:5, 3:2, 16:9, 9:16, "W:H" or a number), taking the largest centred region (or aligned to an edge with `position`). Boxes are kept inside the image. Output JPEG/PNG/WebP/AVIF, name <name>-<W>x<H>.<ext>. Web version: https://s3eidat.github.io/image-resize/

InputTypeDescription
image requiredstringThe image: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
xintegerLeft edge of the crop box (pixels). Use with y, width, height.
yintegerTop edge of the crop box (pixels).
widthintegerWidth of the crop box (pixels).
heightintegerHeight of the crop box (pixels).
ratiostring | numberAspect ratio as "W:H" (e.g. "1:1", "4:5", "3:2", "16:9", "9:16"), a number (width/height), "orig" or "free".
position"center" | "top" | "bottom" | "left" | "right" | "top-left" | "top-right" | "bottom-left" | "bottom-right"
default "center"
Where the ratio crop sits. Default center (the website default).
format"keep" | "jpeg" | "png" | "webp" | "avif"
default "keep"
Output format. "keep" = same as the input when it is JPEG/PNG/WebP (else PNG). Default keep.
qualityinteger
default 90
Quality 1-100 for JPEG / WebP / AVIF (ignored for PNG). Default 90, same as the website.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example: Largest centred square.

{
  "image": "/photos/group.jpg",
  "ratio": "1:1"
}

Example

{
  "image": "/photos/group.jpg",
  "x": 100,
  "y": 50,
  "width": 800,
  "height": 600
}
image_exif_readRead photo metadata (EXIF)

Show the hidden metadata in a photo: camera make/model, lens, date taken, exposure, ISO, software, author/copyright, serial numbers, comments, PNG text chunks, and GPS coordinates (where the photo was taken). Lists every metadata block (EXIF, XMP, ICC, IPTC, embedded previews) with its size and flags privacy-sensitive fields. JPEG and PNG fully; WebP/AVIF/HEIC/TIFF EXIF via sharp. Web version: https://s3eidat.github.io/exif-cleaner/

InputTypeDescription
image requiredstringThe photo: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).

Example: Camera Google Pixel 9, Date taken 2026:10:21 18:42:05, GPS 31.953900, 35.910600.

{
  "image": "/photos/IMG_20261021_184205.jpg"
}
image_exif_stripRemove photo metadata (EXIF)

Remove EXIF, GPS location, XMP, IPTC, comments, PNG text chunks and embedded previews from a photo before sharing it. JPEG and PNG are cleaned losslessly (image data copied byte for byte, like the website); by default the rotation is kept as a tiny orientation-only EXIF and the ICC colour profile is kept. applyOrientation re-encodes JPEGs with the rotation baked into the pixels. WebP/AVIF/HEIC/TIFF are re-encoded without metadata. Output name <name>-clean.<ext>. Web version: https://s3eidat.github.io/exif-cleaner/

InputTypeDescription
image requiredstringThe photo: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
keepOrientationboolean
default true
Keep only the Orientation tag so the photo still shows the right way up. Default true.
keepIccboolean
default true
Keep the ICC colour profile (no personal data; keeps colours right). Default true.
applyOrientationboolean
default false
Instead of keeping the Orientation tag, rotate the pixels (re-encodes JPEG at `quality`; PNG is unaffected). Default false.
qualityinteger
default 92
JPEG/WebP/AVIF quality when re-encoding is needed. Default 92.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example: Writes IMG_1234-clean.jpg: no GPS, camera or date; same pixels.

{
  "image": "/photos/IMG_1234.jpg"
}

Example

{
  "image": "/photos/IMG_1234.jpg",
  "applyOrientation": true
}
image_favicon_setFavicon set generator

Make a complete favicon set from a letter/emoji/short text or from an image: favicon.ico (16, 32, 48 px), favicon-16x16.png, favicon-32x32.png, apple-touch-icon.png (180, square, solid), android-chrome-192x192.png and -512x512.png, site.webmanifest and head-snippet.html with the <link> tags. Rounded square with background colour (or transparent), corner radius and padding in %. Writes everything into one folder. Web version: https://s3eidat.github.io/favicon-forge/

InputTypeDescription
textstring
default "S"
Letter(s) or emoji drawn on the icon (when no image is given). Default "S".
imagestringUse this image (logo) instead of text: a local path or base64. Square, 512 px+ works best.
weight"400" | "500" | "600" | "700" | "800" | "900"
default "900"
Font weight for text. Default 900.
colorstring
default "#ffffff"
Text colour. Default #ffffff.
backgroundstring
default "#0e7c86"
Background colour. Default #0e7c86.
transparentboolean
default false
Transparent background (the Apple icon still gets white). Default false.
radiusnumber
default 22
Corner radius in % of the size (50 = circle). Default 22.
paddingnumber
default 14
Padding around the text/image in %. Default 14.
fit"contain" | "cover"
default "contain"
How an image fills the padded square. Default contain.
namestring
default "My Website"
App name for site.webmanifest. Default "My Website".
shortNamestring
default "Site"
Short name for site.webmanifest. Default "Site".
outputstringFolder to write the files into. Default: a new "favicons" folder in the output folder.

Example

{
  "text": "S3",
  "background": "#0e7c86",
  "radius": 24,
  "padding": 16,
  "name": "S3eidat Tools",
  "shortName": "S3eidat"
}

Result

{
  "folder": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-HmqiFx\\favicons\\",
  "files": [
    {
      "name": "favicon.ico",
      "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-HmqiFx\\favicons\\favicon.ico",
      "bytes": 4462
    },
    {
      "name": "favicon-16x16.png",
      "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-HmqiFx\\favicons\\favicon-16x16.png",
      "bytes": 688
    },
    {
      "name": "favicon-32x32.png",
      "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-HmqiFx\\favicons\\favicon-32x32.png",
      "bytes": 1394
    },
    {
      "name": "apple-touch-icon.png",
      "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-HmqiFx\\favicons\\apple-touch-icon.png",
      "bytes": 6647
    },
    {
      "name": "android-chrome-192x192.png",
      "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-HmqiFx\\favicons\\android-chrome-192x192.png",
      "bytes": 9738
    },
    {
      "name": "android-chrome-512x512.png",
      "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-HmqiFx\\favicons\\android-chrome-512x512.png",
      "bytes": 20070
    },
    {
      "name": "site.webmanifest",
      "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-HmqiFx\\favicons\\site.webmanifest",
      "bytes": 380
    },
    {
      "name": "head-snippet.html",
      "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-HmqiFx\\favicons\\head-snippet.html",
      "bytes": 368
    }
  ],
  "snippet": "<link rel=\"icon\" href=\"/favicon.ico\" sizes=\"48x48\">\n<link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/favicon-32x32.png\">\n<link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/favicon-16x16.png\">\n<link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"/apple-touch-icon.png\">\n<link rel=\"manifest\" href=\"/site.webmanifest\">\n<meta name=\"theme-color\" content=\"#0e7c86\">"
}

Example

{
  "image": "/brand/logo.png",
  "transparent": true,
  "radius": 0,
  "output": "/site/public/"
}
image_from_base64Base64 / data URI to image file

Decode Base64 back into a file: accepts a data URI, a CSS url("data:…") rule, an <img src="data:…"> tag, URL-encoded SVG data URIs, or raw Base64 (standard or URL-safe, line breaks allowed). Detects the real type from the bytes, warns when it differs from the declared one, and writes decoded.<ext>. Give the text inline or as a path to a text file. Web version: https://s3eidat.github.io/image-to-base64/

InputTypeDescription
textstringThe data URI / CSS / <img> tag / raw Base64.
pathstringOr: a text file containing it.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).

Example: Writes decoded.png (1×1).

{
  "text": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
}

Result

{
  "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-BKoKKG\\decoded.png",
  "bytes": 70,
  "mimeType": "image/png",
  "declaredMimeType": "image/png",
  "detectedMimeType": "image/png",
  "isImage": true,
  "width": 1,
  "height": 1,
  "status": "Valid image"
}
image_paletteColour palette from image

Extract the main colours of an image with their share of the picture: k-means (default, deterministic) or median cut, 1-16 colours, sorted by share. Transparent pixels are ignored. Returns hex + rgb + % per colour, CSS custom properties (:root { --color-1 … }) or JSON, and optionally writes the website's swatch strip as palette.png. Same algorithm and sampling as the website, so the colours match. Web version: https://s3eidat.github.io/palette-from-image/

InputTypeDescription
image requiredstringThe image: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
countinteger
default 6
Number of colours. Default 6.
method"kmeans" | "median"
default "kmeans"
kmeans (default) or median (median cut).
format"css" | "json"
default "css"
Export text format. Default css.
swatchPngboolean
default false
Also write palette.png (160×200 swatch per colour with its hex code).
outputstringWhere to write palette.png (file path or folder ending in "/"). Implies swatchPng.

Example: colors: [{ hex: "#1f2f6e", percent: "31.2%" }, …]

{
  "image": "/photos/sunset.jpg",
  "count": 5
}
image_placeholderPlaceholder image

Make a placeholder image of any size (up to 8000 px a side): solid background with centred text (default "W × H", "\n" for line breaks), auto-fitted font size or a fixed one, PNG / JPEG / WebP / SVG. Size presets for web, social media, IAB ads and devices. Optionally returns a data URI / <img> tag for inlining. Text uses Inter when installed, else a system sans-serif. Web version: https://s3eidat.github.io/placeholder-image/

InputTypeDescription
widthinteger
default 1200
Width in pixels. Default 1200.
heightinteger
default 630
Height in pixels. Default 630.
preset"open-graph-link-preview" | "hero-16-9-full-hd" | "hd-16-9" | "blog-image-4-3" | "thumbnail-3-2" | "avatar" | "instagram-square" | "instagram-portrait-4-5" | "story-reel-9-16" | "x-twitter-header" | "linkedin-banner" | "youtube-thumbnail" | "medium-rectangle" | "leaderboard" | "wide-skyscraper" | "mobile-banner" | "half-page" | "billboard" | "iphone-15-screen" | "desktop-1440" | "4k-uhd"Size preset (overrides width/height): open-graph-link-preview 1200×630, hero-16-9-full-hd 1920×1080, hd-16-9 1280×720, blog-image-4-3 800×600, thumbnail-3-2 600×400, avatar 256×256, instagram-square 1080×1080, instagram-portrait-4-5 1080×1350, story-reel-9-16 1080×1920, x-twitter-header 1500×500, linkedin-banner 1584×396, youtube-thumbnail 1280×720, medium-rectangle 300×250, leaderboard 728×90, wide-skyscraper 160×600, mobile-banner 320×50, half-page 300×600, billboard 970×250, iphone-15-screen 1179×2556, desktop-1440 1440×900, 4k-uhd 3840×2160.
backgroundstring
default "#1f2937"
Background colour (#rgb or #rrggbb). Default #1f2937.
colorstring
default "#9ca3af"
Text colour (#rgb or #rrggbb). Default #9ca3af.
textstring
default ""
Text to show. Empty = "W × H". Use "\n" (backslash n) or a real newline for a line break.
fontSizenumber
default 0
Font size in px; 0 = auto-fit (default).
fontWeight"400" | "500" | "600" | "700" | "800" | "900"
default "600"
Font weight. Default 600.
format"png" | "jpeg" | "webp" | "svg"
default "png"
Output format. Default png.
dataUriboolean
default false
Also return the image as a data URI and an <img> tag (for small images).
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example: placeholder-1200x630.png with "1200 × 630".

{
  "width": 1200,
  "height": 630
}

Result

{
  "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-yPrhd0\\placeholder-1200x630.png",
  "bytes": 29305,
  "mimeType": "image/png",
  "width": 1200,
  "height": 630,
  "format": "png",
  "aspectRatio": "40:21",
  "megapixels": "0.76",
  "fontSize": 133
}

Example

{
  "preset": "leaderboard",
  "background": "#0e9f9a",
  "color": "#e6fffd",
  "text": "Ad space",
  "format": "svg"
}

Result

{
  "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-FqID82\\placeholder-728x90.svg",
  "bytes": 421,
  "mimeType": "image/svg+xml",
  "width": 728,
  "height": 90,
  "format": "svg",
  "aspectRatio": "364:45",
  "megapixels": "0.07",
  "fontSize": 31
}
image_resizeResize image

Resize an image by pixels or percent, keeping the aspect ratio. Give width or height (the other side follows), or both: fit "cover" (default, as on the website) centre-crops to that ratio first so nothing is stretched, "contain" fits inside the box, "fill" stretches. Optional crop ratio (1:1, 4:5, 3:2, 16:9, 9:16, orig) or crop box before resizing, social-media presets, output JPEG/PNG/WebP/AVIF. Up to 16384 px a side. Output name: <name>-<W>x<H>.<ext>. Web version: https://s3eidat.github.io/image-resize/

InputTypeDescription
image requiredstringThe image: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
widthintegerTarget width in pixels.
heightintegerTarget height in pixels.
percentnumberScale by percent instead (e.g. 50 halves both sides, 200 doubles).
preset"instagram-post" | "instagram-portrait" | "story" | "youtube-thumbnail" | "x-header" | "facebook-cover" | "linkedin-banner" | "full-hd" | "passport-35x45" | "us-passport-2x2"Exact output size preset: instagram-post 1080×1080, instagram-portrait 1080×1350, story 1080×1920, youtube-thumbnail 1280×720, x-header 1500×500, facebook-cover 851×315, linkedin-banner 1584×396, full-hd 1920×1080, passport-35x45 413×531, us-passport-2x2 600×600.
fit"cover" | "contain" | "fill"
default "cover"
When both width and height are set: cover (crop to fill, default), contain (fit inside), fill (stretch).
ratiostring | numberAspect ratio as "W:H" (e.g. "1:1", "4:5", "3:2", "16:9", "9:16"), a number (width/height), "orig" or "free".
cropobjectCrop box (x, y, width, height in pixels) applied before resizing; overrides ratio.
format"keep" | "jpeg" | "png" | "webp" | "avif"
default "keep"
Output format. "keep" = same as the input when it is JPEG/PNG/WebP (else PNG). Default keep.
qualityinteger
default 90
Quality 1-100 for JPEG / WebP / AVIF (ignored for PNG). Default 90, same as the website.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example: Keeps the aspect ratio: 1600×900 → 1080×608.

{
  "image": "/photos/beach.jpg",
  "width": 1080
}

Example: Centre-crops to 1:1 and writes beach-1080x1080.jpg.

{
  "image": "/photos/beach.jpg",
  "preset": "instagram-post"
}

Example

{
  "image": "/photos/beach.jpg",
  "percent": 50,
  "format": "webp"
}
image_rotateRotate / flip image

Rotate an image clockwise by 90, 180, 270 or any angle (the canvas grows to fit; new corners get `background`), and/or flip it horizontally (mirror) or vertically. Flips apply after the rotation, as seen on screen. EXIF orientation is applied first (autoOrient, default on), so with angle 0 this simply "fixes" sideways phone photos. Output JPEG/PNG/WebP/AVIF ("keep" = same as input), name <name>-rotated.<ext>. Web version: https://s3eidat.github.io/photo-filters/

InputTypeDescription
image requiredstringThe image: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
anglenumber
default 90
Clockwise rotation in degrees (negative = counter-clockwise). Default 90.
flipHorizontalboolean
default false
Mirror left-right (after rotating).
flipVerticalboolean
default false
Mirror top-bottom (after rotating).
autoOrientboolean
default true
Apply the EXIF Orientation tag first (what browsers show). Default true.
backgroundstring
default "#00000000"
Fill for the corners uncovered by a non-right-angle rotation. Default transparent (white in JPEG).
format"keep" | "jpeg" | "png" | "webp" | "avif"
default "keep"
Output format. Default keep (JPEG/PNG/WebP stay, others become PNG).
qualityinteger
default 92
Quality 1-100 for JPEG / WebP / AVIF (ignored for PNG). Default 92, same as the website.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example

{
  "image": "/photos/scan.jpg",
  "angle": 90
}

Example: Un-mirror a selfie.

{
  "image": "/photos/selfie.jpg",
  "angle": 0,
  "flipHorizontal": true
}

Example: Bake the EXIF rotation into the pixels.

{
  "image": "/photos/IMG_1234.jpg",
  "angle": 0
}
image_svg_to_pngSVG to PNG

Rasterise an SVG to a crisp PNG: scale (1×, 2×, 3×, 4×… default 2×), a target width or height (aspect kept), or an exact size (stretched). Units (px, pt, mm, cm, in) and viewBox-only SVGs are understood; transparent background or a solid colour. Scripts, event handlers and external references are removed first. Name <name>@2x.png. Web version: https://s3eidat.github.io/svg-to-png/

InputTypeDescription
svgstringThe SVG: a local path or base64 / data URI.
svgTextstringOr: the SVG code itself.
mode"scale" | "width" | "height" | "exact"
default "scale"
How to size the PNG. Default scale.
scalenumber
default 2
Scale factor for mode "scale". Default 2.
widthintegerWidth in px for modes width / exact.
heightintegerHeight in px for modes height / exact.
transparentboolean
default true
Keep the background transparent. Default true.
backgroundstring
default "#ffffff"
Background colour when transparent is false. Default #ffffff.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example: Writes logo@3x.png.

{
  "svg": "/icons/logo.svg",
  "scale": 3
}

Example: 200×100 PNG.

{
  "svgText": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 50\"/>",
  "mode": "width",
  "width": 200
}

Result

{
  "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-sMIPxv\\image.png",
  "bytes": 179,
  "mimeType": "image/png",
  "width": 200,
  "height": 100,
  "svgWidth": 100,
  "svgHeight": 50
}
image_to_base64Image to Base64 / data URI

Encode an image (or any file) as Base64: a data URI, bare Base64, a CSS background-image rule, an HTML <img> tag (with width/height), Markdown, or for SVG a URL-encoded data URI (usually ~30% smaller). Detects the type from the bytes and warns when the result is too big to inline (over 10 KB: slows pages; over 1 MB: use a normal file). Results longer than maxChars are written to a .txt file instead of being returned. Web version: https://s3eidat.github.io/image-to-base64/

InputTypeDescription
image requiredstringThe image: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
as"dataUri" | "base64" | "css" | "html" | "markdown" | "svgUri"
default "dataUri"
What to return: dataUri (default), base64, css, html, markdown, or svgUri (SVG only).
mimeTypestringOverride the detected MIME type (e.g. image/svg+xml).
maxCharsinteger
default 200000
Longest text returned inline; longer results go to a file. Default 200000.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).

Example: background-image: url("data:image/png;base64,…");

{
  "image": "/site/icons/logo.png",
  "as": "css"
}

Example

{
  "image": "/site/icons/arrow.svg",
  "as": "svgUri"
}
image_watermarkWatermark image

Add a text or logo watermark to a photo. Size is a % of the photo width, margin a % of the shorter side, so the result looks the same on any resolution. One of 9 positions or tiled across the whole image (brick pattern), with opacity, rotation, colour, weight and a soft shadow for text. Logo: any image (PNG with transparency works best). Output keeps the input format (JPEG/PNG/WebP) or as chosen; name <name>-watermarked.<ext>. Text uses the Inter font when installed, else a system sans-serif. Web version: https://s3eidat.github.io/watermark/

InputTypeDescription
image requiredstringThe photo: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
textstring
default "© Your Name"
Watermark text (ignored when a logo is given). Default "© Your Name".
logostringUse this image as the watermark instead of text: a local path or base64.
colorstring
default "#ffffff"
Text colour. Default #ffffff.
weight"400" | "500" | "600" | "700" | "800" | "900"
default "700"
Font weight. Default 700.
shadowboolean
default true
Soft drop shadow behind text. Default true.
position"top-left" | "top" | "top-right" | "left" | "center" | "right" | "bottom-left" | "bottom" | "bottom-right"
default "bottom-right"
Where the watermark sits (ignored when tiled). Default bottom-right.
tiledboolean
default false
Repeat the watermark across the whole photo.
opacitynumber
default 60
Opacity in %. Default 60.
sizenumber
default 28
Watermark width as % of the photo width. Default 28.
rotationnumber
default 0
Rotation in degrees (e.g. -30 for a diagonal tile). Default 0.
marginnumber
default 4
Margin from the edge in % of the shorter side; spacing between tiles when tiled. Default 4.
format"keep" | "jpeg" | "png" | "webp"
default "keep"
Output format. Default keep.
qualityinteger
default 92
Quality 1-100 for JPEG / WebP / AVIF (ignored for PNG). Default 92, same as the website.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example

{
  "image": "/photos/shoot-01.jpg",
  "text": "© Studio Lina",
  "position": "bottom-right",
  "opacity": 60
}

Example

{
  "image": "/photos/proof.jpg",
  "text": "PROOF",
  "tiled": true,
  "rotation": -25,
  "opacity": 35,
  "size": 26,
  "margin": 3
}

Example

{
  "image": "/photos/product.png",
  "logo": "/brand/logo.png",
  "position": "top-left",
  "size": 20
}

Text

Counting, case, slugs, cleaning, diffs, readability, Arabic tools, numbers to words, citations.

text_arabicArabic text tools

Arabic text utilities. mode "clean": remove tashkeel (harakat and Quranic marks, optionally keeping shadda), remove tatweel (kashida), normalise letters (alef with hamza/madda/wasla → bare alef; alef maqsura and Farsi ya → ya; ta marbuta → ha; hamza on waw/ya → waw/ya), switch Latin , ; ? to Arabic punctuation, and convert digits (Arabic-Indic ↔ Western). mode "arabizi": turn Arabizi / Franco-Arabic chat text (mar7aba, 3arabi, 5ala9, el bait) into Arabic script. Returns the result with letter/word/character counts. Give "text" or a text file "path". Web version: https://s3eidat.github.io/arabic-tools/

InputTypeDescription
textstringThe text to transform.
pathstringOr: a path to a UTF-8 text file.
mode"clean" | "arabizi"
default "clean"
"clean" = clean & normalise Arabic text (default); "arabizi" = Arabizi (Latin letters + numbers) → Arabic script.
removeTashkeelboolean
default true
clean: remove tashkeel / harakat (default true).
keepShaddaboolean
default false
clean: when removing tashkeel, keep the shadda.
removeTatweelboolean
default true
clean: remove tatweel / kashida (default true).
normalizeAlefboolean
default false
clean: alef with hamza above/below, madda or wasla → bare alef.
normalizeYaboolean
default false
clean: alef maqsura and Farsi ya → Arabic ya.
taMarbutaToHaboolean
default false
clean: ta marbuta → ha.
normalizeHamzaboolean
default false
clean: hamza on waw → waw, hamza on ya → ya.
arabicPunctuationboolean
default false
clean: Latin comma, semicolon and question mark → Arabic ones.
digits"keep" | "western" | "arabic"
default "keep"
clean: keep digits, convert Arabic-Indic/Persian digits to 0-9 ("western"), or 0-9 to Arabic-Indic ("arabic").
outputstringOptional: write the result to this file path (or folder ending in "/") instead of returning it.

Example

{
  "mode": "arabizi",
  "text": "mar7aba, kifak?"
}

Result

{
  "text": "مرحبا، كيفك؟",
  "arabicLetters": 9,
  "words": 2,
  "characters": 12
}

Example

{
  "text": "2026",
  "digits": "arabic"
}

Result

{
  "text": "٢٠٢٦",
  "arabicLetters": 0,
  "words": 1,
  "characters": 4,
  "marksRemoved": 0
}
text_caseCase convert

Convert text case: upper, lower, title (smart: keeps small words lower and iPhone/NASA as typed), sentence, camel, pascal, snake, kebab, constant, alternate, inverse. Works line by line and handles Unicode (Arabic, accents, ß). Web version: https://s3eidat.github.io/case-convert/

InputTypeDescription
text requiredstringText to convert.
mode required"upper" | "lower" | "title" | "sentence" | "camel" | "pascal" | "snake" | "kebab" | "constant" | "alternate" | "inverse"Target case.

Example

{
  "text": "a tale OF two cities",
  "mode": "title"
}

Result

{
  "result": "A Tale of Two Cities"
}

Example

{
  "text": "XMLParser handler",
  "mode": "snake"
}

Result

{
  "result": "xml_parser_handler"
}
text_citationCitation generator

Format references from structured fields in APA 7, MLA 9, Chicago author-date (17th), Harvard (Cite Them Right) or IEEE, for books, book chapters, journal articles, web pages, newspaper/magazine articles, videos and reports. Handles author-count rules (et al.), initials, organisations as authors, editions, page ranges (MLA 127-53), DOIs, URLs and access dates, and title case for MLA/Chicago/IEEE. Returns each citation as plain text and HTML (italics as <i>), the in-text citation, missing-field hints, and the whole list as a bibliography sorted for the style (numbered for IEEE). Web version: https://s3eidat.github.io/citation-gen/

InputTypeDescription
style required"apa" | "mla" | "chicago" | "harvard" | "ieee"Citation style: apa, mla, chicago, harvard or ieee.
sources requiredobject[]One or more sources.
autoCaseboolean
default true
Apply title case where the style wants it (MLA, Chicago, IEEE book titles). Default true.

Example: Abelson, H., & Sussman, G. J. (1996). Structure and interpretation of computer programs (2nd ed.). MIT Press.

{
  "style": "apa",
  "sources": [
    {
      "type": "book",
      "authors": [
        {
          "first": "Harold",
          "last": "Abelson"
        },
        {
          "first": "Gerald Jay",
          "last": "Sussman"
        }
      ],
      "title": "Structure and interpretation of computer programs",
      "edition": "2",
      "year": "1996",
      "publisher": "MIT Press",
      "place": "Cambridge, MA, USA"
    }
  ]
}

Result

{
  "style": "APA 7",
  "citations": [
    {
      "text": "Abelson, H., & Sussman, G. J. (1996). Structure and interpretation of computer programs (2nd ed.). MIT Press.",
      "html": "Abelson, H., &amp; Sussman, G. J. (1996). <i>Structure and interpretation of computer programs</i> (2nd ed.). MIT Press.",
      "inText": "(Abelson & Sussman, 1996)",
      "missing": []
    }
  ],
  "bibliographyTitle": "References",
  "bibliography": "Abelson, H., & Sussman, G. J. (1996). Structure and interpretation of computer programs (2nd ed.). MIT Press."
}
text_cleanText cleaner

Clean up text line by line with a fixed pipeline (same order as the website): strip HTML tags and decode entities → remove emojis → trim each line → collapse repeated spaces → remove empty lines → remove duplicate lines (optionally ignoring case) → sort (A–Z, Z–A, natural, by length) → reverse → shuffle (seedable) → add line numbers. Returns the cleaned text plus counts, or writes it to a file when "output" is given. Give "text" or a text file "path". Web version: https://s3eidat.github.io/text-cleaner/

InputTypeDescription
textstringThe text to clean.
pathstringOr: a path to a UTF-8 text file.
stripHtmlboolean
default false
Strip HTML tags (drops script/style/comments, block ends become line breaks) and decode entities (default false).
removeEmojiboolean
default false
Remove emojis (©, ® and ™ are kept) (default false).
trimboolean
default true
Trim spaces at the start and end of each line (default true).
collapseSpacesboolean
default true
Collapse runs of spaces/tabs into one space (default true).
removeEmptyLinesboolean
default true
Remove empty (whitespace-only) lines (default true).
removeDuplicatesboolean
default false
Remove duplicate lines, keeping the first (default false).
ignoreCaseboolean
default false
When removing duplicates, treat lines that differ only in case as duplicates (default false).
sort"none" | "az" | "za" | "natural" | "length"
default "none"
Sort lines: none (default), az, za, natural (item 2 before item 10), length (shortest first).
reverseboolean
default false
Reverse the line order (default false).
shuffleboolean
default false
Shuffle the lines (default false).
seedintegerSeed for a reproducible shuffle (default: random).
numberLinesboolean
default false
Prefix each line with its number ("1. ", padded) (default false).
outputstringOptional: write the cleaned text to this file path (or folder ending in "/") instead of returning it.

Example

{
  "text": "item 10\nitem 2\nitem 2\n\nitem 1",
  "removeDuplicates": true,
  "sort": "natural"
}

Result

{
  "text": "item 1\nitem 2\nitem 10",
  "linesIn": 5,
  "linesOut": 3,
  "duplicatesRemoved": 1,
  "emptyLinesRemoved": 1,
  "charactersOut": 21
}
text_diffDiff two texts

Compare two texts line by line (Myers diff) and return a unified diff (3 lines of context, like `diff -u`), counts of added/removed/unchanged lines, and change blocks where each removed line is paired with the added line that replaced it and the changed words are marked [-old-] / {+new+}. Can ignore whitespace and case. Works with any language (Arabic included). Up to 50,000 lines per side. Web version: https://s3eidat.github.io/diff-view/

InputTypeDescription
originalstringThe original text ("before").
originalPathstringOr: path to the original text file.
changedstringThe changed text ("after").
changedPathstringOr: path to the changed text file.
ignoreWhitespaceboolean
default false
Treat lines that differ only in spacing as equal.
ignoreCaseboolean
default false
Treat lines that differ only in letter case as equal.
wordDiffboolean
default true
Mark the changed words inside paired lines (default true).
nameAstringLabel for the "---" header (default "original").
nameBstringLabel for the "+++" header (default "changed").

Example

{
  "original": "one\ntwo\nthree",
  "changed": "zero\none\nthree"
}

Result

{
  "identical": false,
  "added": 1,
  "removed": 1,
  "unchanged": 2,
  "unified": "--- original\n+++ changed\n@@ -1,3 +1,3 @@\n+zero\n one\n-two\n three\n",
  "changes": [
    {
      "removed": [],
      "added": [
        {
          "line": 1,
          "text": "zero"
        }
      ]
    },
    {
      "removed": [
        {
          "line": 2,
          "text": "two"
        }
      ],
      "added": []
    }
  ]
}

Example: changes[0]: removed marked "the [-quick-] fox", added marked "the {+slow+} fox"

{
  "original": "the quick fox",
  "changed": "the slow fox"
}

Result

{
  "identical": false,
  "added": 1,
  "removed": 1,
  "unchanged": 0,
  "unified": "--- original\n+++ changed\n@@ -1 +1 @@\n-the quick fox\n+the slow fox\n",
  "changes": [
    {
      "removed": [
        {
          "line": 1,
          "text": "the quick fox",
          "marked": "the [-quick-] fox"
        }
      ],
      "added": [
        {
          "line": 1,
          "text": "the slow fox",
          "marked": "the {+slow+} fox"
        }
      ]
    }
  ]
}
text_escapeEscape / unescape text

Escape or unescape a string for a target: JSON (string body), JavaScript (incl. template literals), HTML, XML, URL (encodeURIComponent, optional form +), SQL (doubled quotes, optional MySQL backslashes and wrapping quotes), CSV field (RFC 4180; comma, semicolon or tab), regex, POSIX shell single quotes, C/C++, Java/C#, and Unicode escapes (\uXXXX, \u{X}, \UXXXXXXXX, &#xX;, U+XXXX). Options per target as on the website. Set "allTargets" to get the escaped form for every target at once. Web version: https://s3eidat.github.io/text-escape/

InputTypeDescription
text requiredstringThe text to escape, or the escaped text to unescape.
target required"json" | "js" | "html" | "xml" | "url" | "sql" | "csv" | "regex" | "shell" | "c" | "java" | "unicode"Format: json, js, html, xml, url, sql, csv, regex, shell, c, java, unicode.
direction"escape" | "unescape"
default "escape"
escape (default) or unescape.
asciiboolean
default false
json/js/html/c/java: also escape non-ASCII characters (c: as UTF-8 octal).
namedboolean
default false
html (with ascii): prefer named entities such as &copy;.
formboolean
default false
url: form style, + for spaces.
wrapboolean
default false
sql: wrap in single quotes (unescape: strip them).
mysqlboolean
default false
sql: escape backslashes too (MySQL).
alwaysQuoteboolean
default false
csv: always quote the field.
delimiter"," | ";" | "\t"
default ","
csv: field delimiter (comma, semicolon or tab).
unicodeFormat"u4" | "ubrace" | "u8" | "html" | "uplus"
default "u4"
unicode: u4 = \uXXXX (JS, JSON, Java), ubrace = \u{X} (ES6, Rust, Swift), u8 = \UXXXXXXXX (Python, C), html = &#xX;, uplus = U+XXXX.
everyboolean
default false
unicode: escape every character, not only non-ASCII.
allTargetsboolean
default false
escape only: also return the text escaped for every target.

Example

{
  "text": "O'Brien",
  "target": "sql",
  "wrap": true
}

Result

{
  "result": "'O''Brien'",
  "note": "Standard SQL doubles single quotes. Prefer parameterised queries over escaping.",
  "inputCharacters": 7,
  "outputCharacters": 10
}

Example

{
  "text": "a%20b%26c",
  "target": "url",
  "direction": "unescape"
}

Result

{
  "result": "a b&c",
  "note": "encodeURIComponent: for one query value or path segment.",
  "inputCharacters": 9,
  "outputCharacters": 5
}
text_fancyFancy text (Unicode styles)

Restyle text with Unicode look-alike characters for bios and posts (28 styles): Bold, Italic, Bold italic, Script, Bold script, Fraktur, Bold fraktur, Double-struck, Sans-serif, Sans bold, Sans italic, Sans bold italic, Monospace, Circled, Circled dark, Squared, Squared dark, Parenthesized, Fullwidth, Small caps, Superscript, Upside down, Strikethrough, Underline, Double underline, Slashed, Overline, Wide spaced. Only Latin letters and digits change (other scripts such as Arabic and emoji pass through); some styles cover only capitals or only some symbols. Give one "style", or none to get every style. Up to 20,000 characters. Web version: https://s3eidat.github.io/fancy-text/

InputTypeDescription
text requiredstringThe text to restyle.
style"Bold" | "Italic" | "Bold italic" | "Script" | "Bold script" | "Fraktur" | "Bold fraktur" | "Double-struck" | "Sans-serif" | "Sans bold" | "Sans italic" | "Sans bold italic" | "Monospace" | "Circled" | "Circled dark" | "Squared" | "Squared dark" | "Parenthesized" | "Fullwidth" | "Small caps" | "Superscript" | "Upside down" | "Strikethrough" | "Underline" | "Double underline" | "Slashed" | "Overline" | "Wide spaced"One style name (exact, e.g. "Bold", "Script", "Upside down"). Omit to get all styles.

Example

{
  "text": "Hello 2026",
  "style": "Bold"
}

Result

{
  "results": [
    {
      "style": "Bold",
      "text": "𝐇𝐞𝐥𝐥𝐨 𝟐𝟎𝟐𝟔"
    }
  ]
}

Example: Returns all 28 styles.

{
  "text": "Stay curious"
}

Result

{
  "results": [
    {
      "style": "Bold",
      "text": "𝐒𝐭𝐚𝐲 𝐜𝐮𝐫𝐢𝐨𝐮𝐬"
    },
    {
      "style": "Italic",
      "text": "𝑆𝑡𝑎𝑦 𝑐𝑢𝑟𝑖𝑜𝑢𝑠"
    },
    {
      "style": "Bold italic",
      "text": "𝑺𝒕𝒂𝒚 𝒄𝒖𝒓𝒊𝒐𝒖𝒔"
    },
    {
      "style": "Script",
      "text": "𝒮𝓉𝒶𝓎 𝒸𝓊𝓇𝒾ℴ𝓊𝓈"
    },
    {
      "style": "Bold script",
      "text": "𝓢𝓽𝓪𝔂 𝓬𝓾𝓻𝓲𝓸𝓾𝓼"
    },
    {
      "style": "Fraktur",
      "text": "𝔖𝔱𝔞𝔶 𝔠𝔲𝔯𝔦𝔬𝔲𝔰"
    },
    {
      "style": "Bold fraktur",
      "text": "𝕾𝖙𝖆𝖞 𝖈𝖚𝖗𝖎𝖔𝖚𝖘"
    },
    {
      "style": "Double-struck",
      "text": "𝕊𝕥𝕒𝕪 𝕔𝕦𝕣𝕚𝕠𝕦𝕤"
    },
    {
      "style": "Sans-serif",
      "text": "𝖲𝗍𝖺𝗒 𝖼𝗎𝗋𝗂𝗈𝗎𝗌"
    },
    {
      "style": "Sans bold",
      "text": "𝗦𝘁𝗮𝘆 𝗰𝘂𝗿𝗶𝗼𝘂𝘀"
    },
    {
      "style": "Sans italic",
      "text": "𝘚𝘵𝘢𝘺 𝘤𝘶𝘳𝘪𝘰𝘶𝘴"
    },
    {
      "style": "Sans bold italic",
      "text": "𝙎𝙩𝙖𝙮 𝙘𝙪𝙧𝙞𝙤𝙪𝙨"
    },
    {
      "style": "Monospace",
      "text": "𝚂𝚝𝚊𝚢 𝚌𝚞𝚛𝚒𝚘𝚞𝚜"
    },
    {
      "style": "Circled",
      "text": "Ⓢⓣⓐⓨ ⓒⓤⓡⓘⓞⓤⓢ"
    },
    {
      "style": "Circled dark",
      "text": "🅢🅣🅐🅨 🅒🅤🅡🅘🅞🅤🅢"
    },
    {
      "style": "Squared",
      "text": "🅂🅃🄰🅈 🄲🅄🅁🄸🄾🅄🅂"
    },
    {
      "style": "Squared dark",
      "text": "🆂🆃🅰🆈 🅲🆄🆁🅸🅾🆄🆂"
    },
    {
      "style": "Parenthesized",
      "text": "🄢⒯⒜⒴ ⒞⒰⒭⒤⒪⒰⒮"
    },
    {
      "style": "Fullwidth",
      "text": "Stay curious"
    },
    {
      "style": "Small caps",
      "text": "ꜱᴛᴀʏ ᴄᴜʀɪᴏᴜꜱ"
    },
    {
      "style": "Superscript",
      "text": "Sᵗᵃʸ ᶜᵘʳⁱᵒᵘˢ"
    },
    {
      "style": "Upside down",
      "text": "snoᴉɹnɔ ʎɐʇS"
    },
    {
      "style": "Strikethrough",
      "text": "S̶t̶a̶y̶ ̶c̶u̶r̶i̶o̶u̶s̶"
    },
    {
      "style": "Underline",
      "text": "S̲t̲a̲y̲ ̲c̲u̲r̲i̲o̲u̲s̲"
    },
    {
      "style": "Double underline",
      "text": "S̳t̳a̳y̳ ̳c̳u̳r̳i̳o̳u̳s̳"
    },
    {
      "style": "Slashed",
      "text": "S̸t̸a̸y̸ ̸c̸u̸r̸i̸o̸u̸s̸"
    },
    {
      "style": "Overline",
      "text": "S̅t̅a̅y̅ ̅c̅u̅r̅i̅o̅u̅s̅"
    },
    {
      "style": "Wide spaced",
      "text": "S t a y   c u r i o u s"
    }
  ]
}
text_layout_fixKeyboard layout fixer (EN ⇄ AR)

Fix text typed with the wrong keyboard layout between US English (QWERTY) and Arabic (101): "hgsghl ugd;l" becomes the Arabic greeting, and Arabic gibberish typed on English keys becomes "hello". Direction "auto" detects it; in mixed text only the words that look mistyped are converted (using small English/Arabic letter-pair models), so words that are already right stay as they are. Handles the shift layer (harakat, hamza forms), lam-alef keys and Caps Lock (all-caps Latin input is treated as lowercase). Web version: https://s3eidat.github.io/layout-fixer/

InputTypeDescription
textstringThe mistyped text.
pathstringOr: a path to a UTF-8 text file.
direction"auto" | "ar" | "en"
default "auto"
"auto" (default) detects; "ar" = typed on English keys, convert to Arabic; "en" = typed on Arabic keys, convert to English.
capsLockboolean
default false
Caps Lock was on: treat capital Latin letters as lowercase (detected automatically when all letters are capitals).

Example

{
  "text": ";dt phg;?"
}

Result

{
  "text": "كيف حالك؟",
  "direction": "ar",
  "wordsChanged": 2,
  "capsLockIgnored": false
}

Example: Mixed text: only the mistyped Arabic-looking word becomes "hello".

{
  "text": "Hi, ok اثممخ"
}

Result

{
  "text": "Hi, ok hello",
  "direction": "mixed",
  "wordsChanged": 1,
  "capsLockIgnored": false
}
text_loremLorem ipsum generator

Generate placeholder text: classic Lorem ipsum, readable English filler, or Arabic filler. Choose paragraphs (max 100), sentences (max 500) or words (max 10000); optionally start with the classic opener and wrap paragraphs in <p> tags. Give a seed for repeatable output (the same seed gives the same text). Writes a .txt/.html file instead when "output" is given. Web version: https://s3eidat.github.io/lorem-gen/

InputTypeDescription
style"lorem" | "english" | "arabic"
default "lorem"
Word list: lorem (Latin), english, or arabic.
unit"paragraphs" | "sentences" | "words"
default "paragraphs"
What "count" counts.
countinteger
default 3
How many paragraphs / sentences / words (clamped to the unit limit).
openerboolean
default true
Start with the classic opening line ("Lorem ipsum dolor sit amet…", "The quick brown fox…", or the Arabic one). Default true.
htmlboolean
default false
Wrap each paragraph in <p>…</p>.
seedintegerSeed for repeatable output (default: random).
outputstringOptional: write the text to this file path (or folder ending in "/") instead of returning it.

Example: 25 words starting "Lorem ipsum dolor sit amet…"

{
  "unit": "words",
  "count": 25,
  "seed": 42
}

Example

{
  "style": "arabic",
  "unit": "paragraphs",
  "count": 2
}
text_morseMorse code translator

Translate text to International Morse code and back. Encoding: letters separated by spaces, words by " / "; accents are folded (é → E) and characters without a code are skipped and counted. Decoding accepts . and - plus look-alikes (• · – — _), with words split by /, | or 2+ spaces; unknown codes become #. Encoding also returns the playing time at a given speed (PARIS timing: dot = 1200/WPM ms). No audio. Web version: https://s3eidat.github.io/morse-code/

InputTypeDescription
text requiredstringText to encode, or Morse code to decode.
mode"encode" | "decode" | "auto"
default "auto"
"encode" text → Morse, "decode" Morse → text, or "auto" (default): decode if the input only has Morse symbols, spaces, / and |.
wpmnumber
default 18
Speed in words per minute for the duration estimate (default 18).

Example

{
  "text": "SOS",
  "mode": "encode"
}

Result

{
  "mode": "encode",
  "result": "... --- ...",
  "skipped": 0,
  "durationSeconds": 1.8
}

Example

{
  "text": ".... ..  /  - .... . .-. ."
}

Result

{
  "mode": "decode",
  "result": "HI THERE",
  "unrecognized": 0
}
text_number_to_wordsNumber to words (English & Arabic)

Spell a number in words in English and Arabic: English cardinal (with decimals, optional British "and"), ordinal (twenty-first), cheque wording for an amount in a currency (cents in words or as 25/100, optional "only"), Arabic cardinal, and Arabic tafqeet for amounts with correct gender/number agreement of the currency noun (optional "faqat … la ghayr" wrapper and the modern 100 spelling). Currencies: JOD, USD, SAR, AED, KWD, EGP, GBP (or none). Accepts Arabic-Indic digits and thousands separators. Up to 999 trillion (15 digits). Web version: https://s3eidat.github.io/number-words/

InputTypeDescription
number requiredstring | numberThe number or amount, e.g. "1500.250", "1,234,567.89", -12.05 (Arabic-Indic digits work too).
currency"none" | "JOD" | "USD" | "SAR" | "AED" | "KWD" | "EGP" | "GBP"
default "none"
Currency for cheque / tafqeet wording: none (default), JOD, USD, SAR, AED, KWD, EGP, GBP.
onlyboolean
default false
Add "only" (English) and the Arabic "faqat … la ghayr" wrapper to amounts.
britishboolean
default false
British English "and": one hundred and five.
subStyle"words" | "fraction"
default "words"
Cheque sub-units in words (twenty-five cents) or as a fraction (25/100).
modernHundredboolean
default false
Arabic: use the modern spelling of "hundred" (without the extra alif).

Example

{
  "number": "21"
}

Result

{
  "digits": "21",
  "english": "twenty-one",
  "ordinal": "twenty-first",
  "ordinalShort": "21st",
  "arabic": "واحد وعشرون"
}

Example: englishAmount: "One thousand five hundred dollars and twenty-five cents", plus the Arabic tafqeet

{
  "number": "1500.25",
  "currency": "USD"
}

Result

{
  "digits": "1,500.25 USD",
  "english": "one thousand five hundred point two five",
  "arabic": "ألف وخمسمائة فاصلة خمسة وعشرون",
  "englishAmount": "One thousand five hundred dollars and twenty-five cents",
  "arabicAmount": "ألف وخمسمائة دولار وخمسة وعشرون سنتاً"
}
text_readabilityReadability score

Score how easy an English text is to read: Flesch Reading Ease, Flesch-Kincaid Grade, Gunning Fog, SMOG, Coleman-Liau and Automated Readability Index (same formulas, sentence splitter and syllable estimate as the website), plus a verdict, average grade level, and Hemingway-style hints: long sentences (over 20 / 30 words), passive voice and adverbs. The formulas are built for English; for Arabic text the result says so (arabicWarning). Give "text" or a text file "path". Web version: https://s3eidat.github.io/readability/

InputTypeDescription
textstringThe text to score.
pathstringOr: a path to a UTF-8 text file.

Example: Returns all six scores, the verdict and hints.

{
  "text": "Keep it short. Use plain words. Readers scan, so put the main point first and cut what they don't need."
}

Result

{
  "readingEase": 100,
  "verdict": "Very easy",
  "verdictLevel": "ok",
  "advice": "Short sentences and everyday words. Almost anyone can follow it.",
  "averageGrade": 1.7,
  "averageGradeText": "grade 2 · age 7–8",
  "scores": {
    "fleschReadingEase": 111.2,
    "fleschKincaidGrade": {
      "score": -0.6,
      "grade": "kindergarten"
    },
    "gunningFog": {
      "score": 2.7,
      "grade": "grade 3 · age 8–9"
    },
    "smog": {
      "score": 3.1,
      "grade": "grade 3 · age 8–9"
    },
    "colemanLiau": {
      "score": 3,
      "grade": "grade 3 · age 8–9"
    },
    "automatedReadability": {
      "score": 0.5,
      "grade": "grade 1 · age 6–7"
    }
  },
  "words": 20,
  "sentences": 3,
  "syllablesPerWord": 1.05,
  "wordsPerSentence": 6.7,
  "longSentences": [],
  "passive": [],
  "adverbs": [],
  "tips": [
    "No long sentences, passive voice or extra adverbs found."
  ],
  "smogNote": "SMOG is designed for 30+ sentences; with 3 it is a rough estimate."
}
text_slugifySlugify

Turn titles into clean URL slugs, one slug per non-empty line (bulk). Folds accents (é → e, ß → ss), transliterates Arabic to Latin (the article al- becomes its own word) or keeps Arabic letters, converts Arabic-Indic digits, drops tashkeel, turns & into "and", can remove stop words (English and Arabic) and cap the length at a word boundary. Web version: https://s3eidat.github.io/slugify/

InputTypeDescription
text requiredstringTitle(s) to slugify. Each non-empty line becomes one slug.
separator"-" | "_" | "."
default "-"
Word separator: dash (default), underscore or dot.
lowercaseboolean
default true
Lowercase the slug (default true).
foldAccentsboolean
default true
Transliterate accents: é → e, ß → ss, æ → ae (default true).
removeStopWordsboolean
default false
Remove stop words in English and Arabic such as the, of, and (kept if nothing else is left).
arabic"latin" | "keep"
default "latin"
"latin" transliterates Arabic letters (default); "keep" keeps them.
maxLengthinteger
default 0
Maximum slug length in characters, cut at a word boundary (0 = no limit).

Example

{
  "text": "Crème Brûlée & Café au Lait: A Love Story"
}

Result

{
  "slugs": [
    {
      "title": "Crème Brûlée & Café au Lait: A Love Story",
      "slug": "creme-brulee-and-cafe-au-lait-a-love-story",
      "length": 42,
      "encodedLength": 42
    }
  ]
}

Example: lord-rings

{
  "text": "The Lord of the Rings",
  "removeStopWords": true
}

Result

{
  "slugs": [
    {
      "title": "The Lord of the Rings",
      "slug": "lord-rings",
      "length": 10,
      "encodedLength": 10
    }
  ]
}
text_social_countSocial media character counter

Check a post against platform limits the way the platforms count: X/Twitter weighted length (links = 23, emoji = 2, CJK = 2; limit 280), Threads 500, Bluesky 300, Instagram 2,200 (+ 30 hashtags), LinkedIn 3,000, Facebook 63,206, TikTok 4,000, YouTube title 100 / description 5,000, Pinterest 500, Google title ~60, meta description ~160. Characters are counted as user-perceived graphemes (one emoji = 1). Also: words, hashtags, mentions, links, emoji, and SMS encoding (GSM-7 vs Unicode) with the number of SMS parts and the characters that force Unicode. Give "text" or a text file "path". Web version: https://s3eidat.github.io/social-counter/

InputTypeDescription
textstringThe post text.
pathstringOr: a path to a UTF-8 text file.
platforms("x" | "threads" | "bsky" | "ig" | "li" | "fb" | "tt" | "yt" | "ytd" | "pin" | "gt" | "gd")[]Only these meters: x, threads, bsky, ig, li, fb, tt, yt, ytd, pin, gt (Google title), gd (meta description). Default: all.
gsmLookalikesboolean
default false
Also return the text with curly quotes, dashes, ellipses, odd spaces etc. replaced by GSM-7 look-alikes (cheaper SMS).

Example: xWeightedLength 48: the link counts 23 and the emoji 2.

{
  "text": "Read it here: https://example.com/blog/code-reviews 🚀 #coding",
  "platforms": [
    "x"
  ]
}

Result

{
  "characters": 61,
  "words": 9,
  "hashtags": 1,
  "mentions": 0,
  "links": 1,
  "emoji": 1,
  "lines": 1,
  "utf16Units": 62,
  "xWeightedLength": 48,
  "platforms": [
    {
      "id": "x",
      "name": "X (Twitter) post",
      "count": 48,
      "limit": 280,
      "softLimit": false,
      "remaining": 232,
      "over": 0,
      "status": "ok",
      "notes": [
        "Weighted: links 23, emoji 2, CJK 2. Premium allows 25,000."
      ]
    }
  ],
  "sms": {
    "encoding": "UCS-2",
    "parts": 1,
    "unitsUsed": 62,
    "perPart": 70,
    "leftInPart": 8,
    "nonGsmCharacters": [
      {
        "char": "🚀",
        "count": 1
      }
    ]
  }
}
text_word_countWord count

Count words, characters (with and without spaces), sentences, paragraphs, lines and unique words; estimate reading (238 wpm) and speaking (150 wpm) time; list the most used keywords with density. Works for English and Arabic (tashkeel ignored). Give "text" or a text file "path". Web version: https://s3eidat.github.io/word-count/

InputTypeDescription
textstringThe text to analyse.
pathstringOr: a path to a UTF-8 text file.
keywordsinteger
default 12
How many top keywords to return (0 = none).
includeStopWordsboolean
default false
Count common words like "the"/"في" as keywords.

Example: 11 words, 4 sentences, 2 paragraphs

{
  "text": "Hello world. This is a test! Is it?\n\nNew paragraph here."
}

Result

{
  "words": 11,
  "characters": 56,
  "charactersNoSpaces": 45,
  "sentences": 4,
  "paragraphs": 2,
  "lines": 3,
  "uniqueWords": 10,
  "averageWordLength": 3.73,
  "longestWord": "paragraph",
  "readingTimeSeconds": 3,
  "speakingTimeSeconds": 4,
  "keywords": [
    {
      "word": "hello",
      "count": 1,
      "density": 9.09
    },
    {
      "word": "new",
      "count": 1,
      "density": 9.09
    },
    {
      "word": "paragraph",
      "count": 1,
      "density": 9.09
    },
    {
      "word": "test",
      "count": 1,
      "density": 9.09
    },
    {
      "word": "world",
      "count": 1,
      "density": 9.09
    }
  ]
}

Developer: data formats

JSON, CSV, YAML, XML, SQL, HTML/CSS formatting and conversion, types from JSON, curl to code.

css_formatCSS beautify / minify

Beautify CSS (one declaration per line, one selector per line, blank line between top-level rules, nested @media blocks) or minify it. Strings, url(...) and escapes are kept intact; calc() spacing is preserved. Optional comment removal (/*! … */ kept). Give "text" or a file "path". Web version: https://s3eidat.github.io/html-css-format/

InputTypeDescription
textstringThe CSS as inline text.
pathstringOr: a path to a CSS file (UTF-8; use this for big inputs).
mode"beautify" | "minify"
default "beautify"
"beautify" (default) or "minify".
indent"2" | "4" | "tab" | integer
default "2"
Indentation: 2, 4, "tab" (or any number of spaces 0-8).
removeCommentsboolean
default false
Drop comments (/*! … */ license comments are kept).
outputstringOptional: also write the result to this file path (or folder ending in "/"). The result text is then left out of the reply. Results over 1,000,000 characters are always written to a file.

Example

{
  "text": "a,b{color:red;margin:0 auto}"
}

Result

{
  "result": "a,\nb {\n  color: red;\n  margin: 0 auto;\n}"
}
csv_to_jsonCSV to JSON

Convert CSV/TSV to JSON with an RFC 4180 parser (quoted fields, "" escapes, line breaks inside quotes, CR/LF/CRLF, BOM). Detects the delimiter (comma, semicolon, tab, pipe), uses the first row as headers (blank → column_N, duplicates → name_2), and can type values (numbers, true/false, null; long digit strings like IDs stay strings). Output: array of objects or array of arrays, pretty-printed. Give "text" or a file "path". Web version: https://s3eidat.github.io/csv-json/

InputTypeDescription
textstringThe CSV as inline text.
pathstringOr: a path to a CSV/TSV file (UTF-8; use this for big inputs).
delimiter"auto" | "," | ";" | "tab" | "\t" | "|"
default "auto"
Delimiter: "auto" (detect), ",", ";", "tab" or "|".
headerboolean
default true
First row is a header (default true).
typesboolean
default true
Turn numbers, true/false and empty/null into JSON types (default true).
shape"objects" | "arrays"
default "objects"
"objects" (one object per row) or "arrays" (rows as arrays).
outputstringOptional: also write the result to this file path (or folder ending in "/"). The result text is then left out of the reply. Results over 1,000,000 characters are always written to a file.

Example

{
  "text": "id,zip,ok\n1,007,true"
}

Result

{
  "rows": 1,
  "columns": [
    "id",
    "zip",
    "ok"
  ],
  "delimiter": "comma",
  "ragged": false,
  "result": "[\n  {\n    \"id\": 1,\n    \"zip\": \"007\",\n    \"ok\": true\n  }\n]"
}

s3eidatGPT: summarise PDFs, write, translate and ask questions with AI. Try it free.

Open s3eidatGPT
…', line continuations, "copy as curl" from browsers) into code for javascript fetch, axios, python requests, php curl, go net/http or node.js http/https: every target the website supports. understands -x, -h, -d/--data-raw/--data-binary/--data-urlencode, --json, -f forms and files, -u, -b, -a, -e, -g, -i, -k, -l, -m, --connect-timeout, -x, -t, --compressed, --oauth2-bearer. also returns the parsed request (secrets masked unless revealsecrets) and notes on what could not be converted. only parses text; never sends the request. web version: https://s3eidat.github.io/curl-convert/">curl_to_codecURL to code

Convert a curl command (bash/zsh quoting incl.

…', line continuations, "Copy as cURL" from browsers) into code for JavaScript fetch, axios, Python requests, PHP cURL, Go net/http or Node.js http/https: every target the website supports. Understands -X, -H, -d/--data-raw/--data-binary/--data-urlencode, --json, -F forms and files, -u, -b, -A, -e, -G, -I, -k, -L, -m, --connect-timeout, -x, -T, --compressed, --oauth2-bearer. Also returns the parsed request (secrets masked unless revealSecrets) and notes on what could not be converted. Only parses text; never sends the request. Web version: https://s3eidat.github.io/curl-convert/

InputTypeDescription
command requiredstringThe curl command, e.g. "curl -X POST https://api.example.com -H 'Content-Type: application/json' -d '{"a":1}'".
language"fetch" | "axios" | "python" | "php" | "go" | "node" | "all"
default "fetch"
Target: fetch (browser/Node fetch), axios, python (requests), php (cURL), go (net/http), node (http/https), or "all".
revealSecretsboolean
default false
Show Authorization/Cookie/API-key header values and the basic-auth password in the parsed summary (the code always contains them).
outputstringOptional: write the code to this file (single language only).

Example: requests.get(url, auth=("user", "pass"))

{
  "command": "curl -u user:pass https://s3eidat.github.io",
  "language": "python"
}

Result

{
  "method": "GET",
  "url": "https://s3eidat.github.io",
  "headers": [],
  "body": null,
  "basicAuth": {
    "user": "user",
    "password": "••••••••"
  },
  "warnings": [],
  "notConverted": [],
  "code": "import requests\n\nurl = \"https://s3eidat.github.io\"\n\nresponse = requests.get(url, auth=(\"user\", \"pass\"))\nprint(response.status_code)\nprint(response.text)"
}
html_formatHTML beautify / minify

Beautify (indent block elements, keep inline runs on one line) or minify HTML with a forgiving tokenizer: implied end tags (<p>, <li>, <td>…), void elements, and <script>, <style>, <pre>, <textarea> contents kept byte-for-byte. Whitespace between attributes is normalised; attribute values are never changed. Optional comment removal (IE conditional comments kept). Text only: nothing is rendered or run. Give "text" or a file "path". Web version: https://s3eidat.github.io/html-css-format/

InputTypeDescription
textstringThe HTML as inline text.
pathstringOr: a path to a HTML file (UTF-8; use this for big inputs).
mode"beautify" | "minify"
default "beautify"
"beautify" (default) or "minify".
indent"2" | "4" | "tab" | integer
default "2"
Indentation: 2, 4, "tab" (or any number of spaces 0-8).
removeCommentsboolean
default false
Drop comments (IE conditional comments are kept).
outputstringOptional: also write the result to this file path (or folder ending in "/"). The result text is then left out of the reply. Results over 1,000,000 characters are always written to a file.

Example

{
  "text": "<div><p>Hi <b>there</b></p><ul><li>a<li>b</ul></div>"
}

Result

{
  "result": "<div>\n  <p>Hi <b>there</b></p>\n  <ul>\n    <li>a\n    <li>b\n  </ul>\n</div>"
}
json_formatJSON format / minify / validate

Validate, pretty-print (2 or 4 spaces, tab) or minify JSON, optionally sorting object keys (deeply). Invalid JSON gives a clear error with line and column and the lines around it. Also returns size, key count, depth and value count. Give "text" or a file "path"; "output" writes the result to a file. Uses the standard JSON parser (no comments or trailing commas). Web version: https://s3eidat.github.io/json-studio/

InputTypeDescription
textstringThe JSON as inline text.
pathstringOr: a path to a JSON file (UTF-8; use this for big inputs).
mode"format" | "minify" | "validate"
default "format"
"format" pretty-prints, "minify" removes all whitespace, "validate" only checks (never throws on bad JSON).
indent"2" | "4" | "tab" | integer
default "2"
Indentation: 2, 4, "tab" (or any number of spaces 0-8).
sortKeysboolean
default false
Sort object keys alphabetically at every level.
outputstringOptional: also write the result to this file path (or folder ending in "/"). The result text is then left out of the reply. Results over 1,000,000 characters are always written to a file.

Example

{
  "text": "{\"b\":1,\"a\":[1,2]}",
  "sortKeys": true
}

Result

{
  "valid": true,
  "result": "{\n  \"a\": [\n    1,\n    2\n  ],\n  \"b\": 1\n}"
}

Example: valid: false, line 2, column 1

{
  "text": "{\"a\":1,\n}",
  "mode": "validate"
}

Result

{
  "valid": false,
  "error": "Expected double-quoted property name in JSON at position 8",
  "line": 2,
  "column": 1
}
json_queryJSON query (JSONPath)

Pick values out of JSON with a JSONPath expression. Supported: $ (root, optional), .key, ['key'], [0], [-1], [*], .*, ..key (recursive), [start:end:step] slices, unions [0,2] / ['a','b'], and filters like [?(@.price < 10 && @.tags)] (==, !=, <, <=, >, >=, =~ /regex/i, &&, ||, !, .length). Returns each match with its path (the same path style the website's tree view copies, e.g. $.store.book[0].title). Give "text" or a file "path". Web version: https://s3eidat.github.io/json-studio/

InputTypeDescription
textstringThe JSON as inline text.
pathstringOr: a path to a JSON file (UTF-8; use this for big inputs).
query requiredstringJSONPath expression, e.g. "$.store.book[*].author" or "$..book[?(@.price < 10)].title".
limitinteger
default 1000
Return at most this many matches (default 1000).
outputstringOptional: write the matched values as a JSON array to this file.

Example

{
  "text": "{\"store\":{\"book\":[{\"title\":\"A\",\"price\":8},{\"title\":\"B\",\"price\":12}]}}",
  "query": "$..book[?(@.price < 10)].title"
}

Result

{
  "count": 1,
  "matches": [
    {
      "path": "$.store.book[0].title",
      "value": "A"
    }
  ]
}
json_to_csvJSON to CSV

Convert JSON to CSV. An array of objects becomes one row per object with nested objects/arrays flattened to dotted columns (address.city, tags.0); an array of arrays is written as-is. Fields are quoted only when needed (delimiter, quotes, line breaks, leading/trailing spaces); CRLF line ends. A formula guard (on by default) prefixes cells starting with = + - @ so spreadsheets don't run them. Give "text" or a file "path". Web version: https://s3eidat.github.io/csv-json/

InputTypeDescription
textstringThe JSON (array or object) as inline text.
pathstringOr: a path to a JSON file (UTF-8; use this for big inputs).
delimiter"auto" | "," | ";" | "tab" | "\t" | "|"
default "auto"
Delimiter: "auto" (comma), ",", ";", "tab" or "|".
headerboolean
default true
Include a header row (default true).
safeboolean
default true
Formula guard: prefix cells like =SUM(A1) with ' (default true).
bomboolean
default false
Start with a UTF-8 BOM so Excel reads non-English text correctly.
outputstringOptional: also write the result to this file path (or folder ending in "/"). The result text is then left out of the reply. Results over 1,000,000 characters are always written to a file.

Example

{
  "text": "[{\"a\":{\"b\":1},\"s\":\"say \\\"hi\\\"\"}]"
}

Result

{
  "rows": 1,
  "columns": [
    "a.b",
    "s"
  ],
  "result": "a.b,s\r\n1,\"say \"\"hi\"\"\"\r\n"
}
json_to_typesJSON to types (TypeScript, Go, Python, Zod)

Generate type definitions from a JSON sample: TypeScript interfaces (or type aliases), Go structs with json tags (initialisms like ID/URL, pointers for optional/nullable, optional omitempty), Python dataclasses (snake_case fields with the original key noted) and Zod schemas. All items of an array are merged into one type (missing keys become optional, null makes a field nullable, int+float → number/float64, mixed values → unions); identical shapes share one name. These four are every language the website supports. Give "text" or a file "path". Web version: https://s3eidat.github.io/json-to-types/

InputTypeDescription
textstringThe JSON sample as inline text.
pathstringOr: a path to a JSON file (UTF-8; use this for big inputs).
language"typescript" | "go" | "python" | "zod" | "all"
default "typescript"
Output language, or "all" for all four.
rootNamestring
default "Root"
Name of the top-level type (turned into PascalCase).
exportboolean
default true
TypeScript / Zod: add the export keyword.
tsTypeAliasboolean
default false
TypeScript: use "type X = {…}" instead of "interface X {…}".
goOmitEmptyboolean
default true
Go: add ,omitempty to optional fields.
outputstringOptional: write the code to this file (with language "all", the four outputs joined by headers).

Example

{
  "text": "[{\"a\":1},{\"b\":true}]",
  "rootName": "Root",
  "export": false
}

Result

{
  "types": 1,
  "result": "type Root = RootItem[];\n\ninterface RootItem {\n  a?: number;\n  b?: boolean;\n}\n"
}
json_to_yamlJSON to YAML

Convert JSON to YAML (js-yaml dump: no line wrapping, no anchors; strings that would read as numbers/booleans are quoted). Options: 2 or 4 space indent, sort keys, and write a top-level array as a multi-document YAML stream (--- per item). Invalid JSON gives line and column. Give "text" or a file "path". Web version: https://s3eidat.github.io/yaml-json/

InputTypeDescription
textstringThe JSON as inline text.
pathstringOr: a path to a JSON file (UTF-8; use this for big inputs).
indent2 | 4 | "2" | "4"
default 2
Indentation: 2 or 4 spaces.
sortKeysboolean
default false
Sort mapping keys alphabetically.
multiDocumentboolean
default false
A top-level JSON array becomes one YAML document per item.
outputstringOptional: also write the result to this file path (or folder ending in "/"). The result text is then left out of the reply. Results over 1,000,000 characters are always written to a file.

Example

{
  "text": "{\"b\":1,\"a\":{\"d\":1,\"c\":2}}",
  "sortKeys": true
}

Result

{
  "documents": 1,
  "result": "a:\n  c: 2\n  d: 1\nb: 1\n"
}
markdown_tableMarkdown table

Make a GitHub-flavoured Markdown table from CSV, TSV, a JSON array (of objects or of arrays), an existing Markdown table (to re-align / re-pad it) or a "rows" array. Per-column alignment (left, center, right), columns padded to line up (optional), pipes escaped as \| and line breaks as <br>. CSV delimiter (comma, semicolon, tab) is detected from the first line. Give "text", a file "path" or "rows". Web version: https://s3eidat.github.io/markdown-table/

InputTypeDescription
textstringThe CSV / TSV / JSON / Markdown table as inline text.
pathstringOr: a path to a CSV/TSV/JSON/Markdown file (UTF-8; use this for big inputs).
rows((string | number | boolean | null)[])[]Or: the table as an array of rows (first row = header unless header is false).
format"auto" | "csv" | "tsv" | "json" | "markdown"
default "auto"
Input format; "auto" detects Markdown, then JSON, then CSV/TSV.
headerboolean
default true
First row is the header. When false, "Column 1", "Column 2"… headers are added.
align"none" | "left" | "center" | "right" | ("none" | "left" | "center" | "right")[]Column alignment: one value for all columns or one per column. Default: keep a Markdown input's alignment, else none.
prettyboolean
default true
Pad cells so the columns line up (default true).
outputstringOptional: also write the result to this file path (or folder ending in "/"). The result text is then left out of the reply. Results over 1,000,000 characters are always written to a file.

Example

{
  "text": "Name,Qty\nApple,3",
  "align": [
    "left",
    "right"
  ]
}

Result

{
  "columns": 2,
  "rows": 1,
  "result": "| Name  | Qty |\n| :---- | --: |\n| Apple |   3 |"
}
markdown_table_parseMarkdown table to JSON / CSV

Read a Markdown table (pipes optional at the ends, \| escapes, <br> as line breaks) back into data: JSON objects keyed by the header, JSON arrays, or CSV; also returns each column's alignment. Uses the website's Markdown import. Give "text" or a file "path". Web version: https://s3eidat.github.io/markdown-table/

InputTypeDescription
textstringThe Markdown table as inline text.
pathstringOr: a path to a Markdown file (UTF-8; use this for big inputs).
as"objects" | "arrays" | "csv"
default "objects"
"objects" (header → keys), "arrays" (header row first) or "csv".
outputstringOptional: also write the result to this file path (or folder ending in "/"). The result text is then left out of the reply. Results over 1,000,000 characters are always written to a file.

Example

{
  "text": "| Name | Qty |\n| :-- | --: |\n| Apple | 3 |"
}

Result

{
  "columns": [
    "Name",
    "Qty"
  ],
  "aligns": [
    "left",
    "right"
  ],
  "rows": 1,
  "data": [
    {
      "Name": "Apple",
      "Qty": "3"
    }
  ]
}
sql_formatSQL format / minify

Format SQL (one clause per line, indented sub-queries, CASE blocks, JOIN … ON with AND/OR, CREATE TABLE column lists) or minify it to one line per statement. Options: keyword case (UPPER, lower, keep), indent (2, 4, tab), trailing or leading commas, keep/drop comments when minifying. Dialect-neutral tokenizer: understands -- and /* */ comments, '' and E'\'' strings, "x", `x` and [x] identifiers, $ dollar quotes, ::casts, JSON operators, :name/$1/? parameters. Only re-spaces; never runs anything. Give "text" or a file "path". Web version: https://s3eidat.github.io/sql-format/

InputTypeDescription
textstringThe SQL as inline text.
pathstringOr: a path to a .sql file (UTF-8; use this for big inputs).
mode"format" | "minify"
default "format"
"format" (default) or "minify".
keywordCase"upper" | "lower" | "preserve"
default "upper"
Keyword and built-in function case.
indent"2" | "4" | "tab" | 2 | 4
default "2"
Indentation: 2, 4 or "tab".
commas"trailing" | "leading"
default "trailing"
Put list commas at line ends (trailing) or starts (leading).
keepCommentsboolean
default true
Minify only: keep comments (as /* */). Optimizer hints /*+ */ and /*! */ are always kept.
outputstringOptional: also write the result to this file path (or folder ending in "/"). The result text is then left out of the reply. Results over 1,000,000 characters are always written to a file.

Example

{
  "text": "select a, b from t where x = 1 and y = 2"
}

Result

{
  "statements": 1,
  "lines": 8,
  "result": "SELECT\n  a,\n  b\nFROM\n  t\nWHERE\n  x = 1\n  AND y = 2"
}
xml_formatXML format / minify / validate / to JSON

Validate XML (well-formedness and namespaces, with line and column for errors), pretty-print it (2, 4 spaces or tab; mixed content, CDATA and xml:space="preserve" kept exactly as written), minify it, or convert it to JSON (attributes as "@name", repeated elements as arrays, text as "#text"). Optionally removes comments. Never loads external DTDs or entities. Give "text" or a file "path". Web version: https://s3eidat.github.io/xml-format/

InputTypeDescription
textstringThe XML as inline text.
pathstringOr: a path to a XML file (UTF-8; use this for big inputs).
mode"format" | "minify" | "json" | "validate"
default "format"
"format" pretty-prints, "minify" strips layout whitespace, "json" converts to JSON, "validate" only checks (never throws on bad XML).
indent"2" | "4" | "tab" | integer
default "2"
Indentation: 2, 4, "tab" (or any number of spaces 0-8).
removeCommentsboolean
default false
Drop <!-- comments --> (format and minify).
outputstringOptional: also write the result to this file path (or folder ending in "/"). The result text is then left out of the reply. Results over 1,000,000 characters are always written to a file.

Example

{
  "text": "<r><a>1</a><b x='1'  y=\"2\"><c/></b></r>"
}

Result

{
  "valid": true,
  "elements": 4,
  "depth": 3,
  "result": "<r>\n  <a>1</a>\n  <b x='1' y=\"2\">\n    <c/>\n  </b>\n</r>"
}

Example

{
  "text": "<a>\n  <b></a>",
  "mode": "validate"
}

Result

{
  "valid": false,
  "error": "Opening and ending tag mismatch: b line 2 and a",
  "line": 2,
  "column": 6
}
xml_queryXML XPath query

Run an XPath 1.0 expression on XML: node-sets (elements, attributes, text…), numbers (count(), sum()), strings and booleans. Full XPath 1.0: all axes, predicates, operators and the core function library. Namespace prefixes declared anywhere in the document can be used as-is; a default namespace on the root gets the prefix "d" (e.g. //d:book), as on the website. Each matched node comes back as XML text plus its type, name and XPath location. Web version: https://s3eidat.github.io/xml-format/

InputTypeDescription
textstringThe XML as inline text.
pathstringOr: a path to a XML file (UTF-8; use this for big inputs).
xpath requiredstringXPath 1.0 expression, e.g. "//book[price > 30]/title", "count(//item)", "//d:entry/@id".
namespacesobjectExtra or overriding prefix → namespace URI bindings, e.g. {"atom":"http://www.w3.org/2005/Atom"}.
limitinteger
default 1000
Return at most this many nodes (default 1000).

Example

{
  "text": "<lib><book><price>42</price><title>A</title></book><book><price>9</price><title>B</title></book></lib>",
  "xpath": "//book[price > 30]/title"
}

Result

{
  "kind": "nodes",
  "count": 1,
  "nodes": [
    {
      "type": "element",
      "name": "title",
      "location": "/lib/book[1]/title",
      "text": "<title>A</title>"
    }
  ]
}
yaml_to_jsonYAML to JSON

Convert YAML to JSON (YAML 1.2 core schema plus anchors/aliases, << merge keys, timestamps, multi-document files). Safe: no code tags (!!js/function, !!python… are rejected), alias bombs and recursive aliases are stopped. Several documents become a JSON array. Errors give line and column. Give "text" or a file "path". Web version: https://s3eidat.github.io/yaml-json/

InputTypeDescription
textstringThe YAML as inline text.
pathstringOr: a path to a YAML file (UTF-8; use this for big inputs).
indent2 | 4 | "2" | "4"
default 2
Indentation: 2 or 4 spaces.
sortKeysboolean
default false
Sort object keys alphabetically at every level.
alwaysArrayboolean
default false
Always output an array of documents, even for one document.
outputstringOptional: also write the result to this file path (or folder ending in "/"). The result text is then left out of the reply. Results over 1,000,000 characters are always written to a file.

Example

{
  "text": "base: &b {x: 1}\nchild:\n  <<: *b\n  y: 2\n"
}

Result

{
  "documents": 1,
  "values": 6,
  "result": "{\n  \"base\": {\n    \"x\": 1\n  },\n  \"child\": {\n    \"x\": 1,\n    \"y\": 2\n  }\n}\n"
}

Developer: utilities

Hashes, Base64, URLs, JWT, IDs, regex, cron, timestamps, subnets, chmod, HTTP status, .gitignore.

base_convertNumber base convert

Convert a whole number of any size (BigInt) between bases 2–36: binary, octal, decimal, hex, base 36 or any other. Input base can be auto-detected from 0x / 0b / 0o prefixes; spaces, _ and , separators are ignored; negatives OK. Also shows bit length and the two's complement view (signed/unsigned value, hex, bits) at 8/16/32/64 bits, and whether it fits. Fractions are not supported (same as the website). Web version: https://s3eidat.github.io/base-convert/

InputTypeDescription
value requiredstringThe number, e.g. "0xC0FFEE", "-0b101", "1_000_000", "zz" (with from=36).
from"auto" | integer
default "auto"
Input base 2–36, or "auto" (prefix, else decimal).
tointeger[]
default [2,8,10,16,36]
Output bases. Default [2, 8, 10, 16, 36].
groupboolean
default false
Group digits like the website (binary/hex by 4, octal by 3, decimal with commas).
uppercaseboolean
default false
Upper-case letters for bases above 10.
width8 | 16 | 32 | 64
default 32
Bit width for the two's complement view.

Example: decimal 12648430, binary 110000001111111111101110

{
  "value": "0xC0FFEE"
}

Result

{
  "decimal": "12648430",
  "results": [
    {
      "base": 2,
      "name": "Binary",
      "value": "110000001111111111101110"
    },
    {
      "base": 8,
      "name": "Octal",
      "value": "60177756"
    },
    {
      "base": 10,
      "name": "Decimal",
      "value": "12648430"
    },
    {
      "base": 16,
      "name": "Hex",
      "value": "c0ffee"
    },
    {
      "base": 36,
      "name": "Base 36",
      "value": "7j3la"
    }
  ],
  "bitLength": 24,
  "bytes": 3,
  "twosComplement": {
    "width": 32,
    "signed": "12648430",
    "unsigned": "12648430",
    "hex": "0x00C0 FFEE",
    "binary": "00000000110000001111111111101110",
    "fits": true,
    "note": "Fits in 32 bits. Signed range -2147483648 to 2147483647."
  }
}

Example: two’s complement hex FF

{
  "value": "-1",
  "width": 8
}

Result

{
  "decimal": "-1",
  "results": [
    {
      "base": 2,
      "name": "Binary",
      "value": "-1"
    },
    {
      "base": 8,
      "name": "Octal",
      "value": "-1"
    },
    {
      "base": 10,
      "name": "Decimal",
      "value": "-1"
    },
    {
      "base": 16,
      "name": "Hex",
      "value": "-1"
    },
    {
      "base": 36,
      "name": "Base 36",
      "value": "-1"
    }
  ],
  "bitLength": 1,
  "bytes": 1,
  "twosComplement": {
    "width": 8,
    "signed": "-1",
    "unsigned": "255",
    "hex": "0xFF",
    "binary": "11111111",
    "fits": true,
    "note": "Fits in 8 bits. Signed range -128 to 127."
  }
}
base64_decodeBase64 decode

Decode Base64 (standard or URL-safe, padding optional, whitespace and data: URI prefix allowed). Valid UTF-8 text is returned inline; binary data (or anything when "output" is given) is written to a file whose extension comes from the detected type (PNG, JPEG, GIF, WebP, BMP, PDF, ZIP, gzip, MP3, Ogg, MP4, WOFF2, SVG, JSON). Web version: https://s3eidat.github.io/base64-kit/

InputTypeDescription
base64 requiredstringThe Base64 text to decode (may be a data: URI).
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
fileNamestringBase name for the written file (extension follows the detected type). Default "decoded".

Example

{
  "base64": "Zm9vYmFy"
}

Result

{
  "text": "foobar",
  "bytes": 6,
  "isText": true,
  "detectedType": "Text",
  "mimeType": "text/plain"
}

Example: Writes decoded.png

{
  "base64": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=="
}

Result

{
  "bytes": 70,
  "isText": false,
  "detectedType": "PNG image",
  "mimeType": "image/png",
  "dataUriMime": "image/png",
  "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-2G2NgY\\decoded.png"
}
base64_encodeBase64 encode

Encode text (as UTF-8) or a file (path or base64) to Base64. Options: URL-safe alphabet (- and _, no padding), wrap at 76 columns (MIME), or a data: URI for files (MIME type detected from magic bytes). Results over 1 MB, or when "output" is given, are written to a text file instead of returned inline. Web version: https://s3eidat.github.io/base64-kit/

InputTypeDescription
textstringText to encode (UTF-8).
filestringOr: a file to encode: local path or base64 data.
urlSafeboolean
default false
Use the URL-safe alphabet (- and _) and drop "=" padding.
wrapboolean
default false
Insert a line break every 76 characters (MIME style).
dataUriboolean
default false
Return a data: URI (data:<mime>;base64,…) instead of bare Base64.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).

Example

{
  "text": "foobar"
}

Result

{
  "base64": "Zm9vYmFy",
  "inputBytes": 6,
  "characters": 8
}

Example: URL-safe output without padding

{
  "text": "ûÿ",
  "urlSafe": true
}

Result

{
  "inputBytes": 4,
  "characters": 6,
  "base64": "w7vDvw"
}
bytes_convertText ⇄ UTF-8 bytes

Turn text into its UTF-8 bytes (hex, binary or decimal), or bytes back into text. Byte input is lenient: "48 65", "4865", "0x48,0x69", "01001000 01101001", "72 105". Invalid UTF-8 is reported and shown with � replacements. Web version: https://s3eidat.github.io/base-convert/

InputTypeDescription
textstringText to turn into bytes.
bytesstringOr: bytes to turn into text, in the chosen format.
format"hex" | "bin" | "dec"
default "hex"
Byte format. Default hex.
uppercaseboolean
default false
Upper-case hex digits.

Example

{
  "text": "Hi ☕",
  "uppercase": true
}

Result

{
  "text": "Hi ☕",
  "bytes": "48 69 20 E2 98 95",
  "byteCount": 6,
  "characters": 4,
  "validUtf8": true
}
chmod_calcchmod calculator

Convert Unix file permissions between octal (755, 0644, 4755), symbolic (rwxr-xr-x, ls -l style -rwsr-xr-x) and chmod clauses (u=rwx,g=rx,o=; relative ones like g+w or +x apply to "base"). Returns all forms, a plain-English description per owner/group/others, setuid/setgid/sticky meaning, safety warnings, and ready chmod commands. Web version: https://s3eidat.github.io/chmod-calc/

InputTypeDescription
mode requiredstringOctal ("755", "0644", "2775"), symbolic ("rwxr-x---", "drwxrwxrwt") or clauses ("u=rw,go=r", "+x").
basestringStarting mode (octal) for relative clauses like "g+w". Default 000.
filestring
default "file"
File name for the example chmod commands (shell-quoted).
recursiveboolean
default false
Add -R to the example commands.

Example: symbolic rwsr-xr-x, clauses u=rwxs,g=rx,o=rx, setuid explained

{
  "mode": "4755"
}

Result

{
  "octal": "4755",
  "symbolic": "rwsr-xr-x",
  "clauses": "u=rwxs,g=rx,o=rx",
  "description": [
    {
      "who": "Owner",
      "text": "can read, write and execute"
    },
    {
      "who": "Group",
      "text": "can read and execute"
    },
    {
      "who": "Others",
      "text": "can read and execute"
    },
    {
      "who": "setuid",
      "text": "Runs as the file's owner"
    }
  ],
  "warnings": [],
  "commands": {
    "octal": "chmod 4755 file",
    "symbolic": "chmod u=rwxs,g=rx,o=rx file"
  }
}

Example: octal 750

{
  "mode": "g-w,o-rwx",
  "base": "775",
  "file": "/srv/app"
}

Result

{
  "octal": "750",
  "symbolic": "rwxr-x---",
  "clauses": "u=rwx,g=rx,o=",
  "description": [
    {
      "who": "Owner",
      "text": "can read, write and execute"
    },
    {
      "who": "Group",
      "text": "can read and execute"
    },
    {
      "who": "Others",
      "text": "has no access"
    }
  ],
  "warnings": [],
  "commands": {
    "octal": "chmod 750 /srv/app",
    "symbolic": "chmod u=rwx,g=rx,o= /srv/app"
  }
}
cron_explainCron explain

Explain a standard 5-field cron expression (minute hour day-of-month month day-of-week; names like MON/JAN, ranges, lists, steps, ? and the @hourly/@daily/@weekly/@monthly/@yearly/@reboot macros) in plain English and list the next run times in an IANA timezone. Follows Vixie cron: when both day fields are restricted a day matches either one. Quartz 6/7-field and L/W/# syntax are rejected with a clear message; per-field errors are reported. Web version: https://s3eidat.github.io/cron-explain/

InputTypeDescription
expression requiredstringThe cron expression, e.g. "*/30 9-17 * * 1-5" or "@daily".
countinteger
default 10
How many upcoming run times to list (0–100). Default 10.
timezonestringIANA timezone the cron daemon runs in. Default: the server's (Asia/Amman).
fromstringStart searching after this instant (ISO 8601). Default: now.
hour12boolean
default false
Describe times on a 12-hour clock (9:00 AM) instead of 24-hour (09:00).

Example: description: "At 09:00, on Monday through Friday"

{
  "expression": "0 9 * * 1-5",
  "count": 3,
  "timezone": "Europe/London"
}

Result

{
  "valid": true,
  "description": "At 09:00, on Monday through Friday.",
  "timezone": "Europe/London",
  "expanded": "0 9 * * 1-5",
  "nextRuns": [
    {
      "iso": "2026-01-16T09:00:00+00:00",
      "utc": "2026-01-16T09:00:00.000Z",
      "local": "Fri, Jan 16, 2026, 09:00",
      "relative": "in 24 hours"
    },
    {
      "iso": "2026-01-19T09:00:00+00:00",
      "utc": "2026-01-19T09:00:00.000Z",
      "local": "Mon, Jan 19, 2026, 09:00",
      "relative": "in 4 days"
    },
    {
      "iso": "2026-01-20T09:00:00+00:00",
      "utc": "2026-01-20T09:00:00.000Z",
      "local": "Tue, Jan 20, 2026, 09:00",
      "relative": "in 5 days"
    }
  ]
}

Example: description: "At 00:00, on day 13 of the month or on Friday"

{
  "expression": "0 0 13 * 5"
}

Result

{
  "valid": true,
  "description": "At 00:00, on day 13 of the month or on Friday.",
  "timezone": "Asia/Amman",
  "expanded": "0 0 13 * 5",
  "nextRuns": [
    {
      "iso": "2026-01-16T00:00:00+03:00",
      "utc": "2026-01-15T21:00:00.000Z",
      "local": "Fri, Jan 16, 2026, 24:00",
      "relative": "in 12 hours"
    },
    {
      "iso": "2026-01-23T00:00:00+03:00",
      "utc": "2026-01-22T21:00:00.000Z",
      "local": "Fri, Jan 23, 2026, 24:00",
      "relative": "in 7 days"
    },
    {
      "iso": "2026-01-30T00:00:00+03:00",
      "utc": "2026-01-29T21:00:00.000Z",
      "local": "Fri, Jan 30, 2026, 24:00",
      "relative": "in 14 days"
    },
    {
      "iso": "2026-02-06T00:00:00+03:00",
      "utc": "2026-02-05T21:00:00.000Z",
      "local": "Fri, Feb 6, 2026, 24:00",
      "relative": "in 21 days"
    },
    {
      "iso": "2026-02-13T00:00:00+03:00",
      "utc": "2026-02-12T21:00:00.000Z",
      "local": "Fri, Feb 13, 2026, 24:00",
      "relative": "in 28 days"
    },
    {
      "iso": "2026-02-20T00:00:00+03:00",
      "utc": "2026-02-19T21:00:00.000Z",
      "local": "Fri, Feb 20, 2026, 24:00",
      "relative": "in 35 days"
    },
    {
      "iso": "2026-02-27T00:00:00+03:00",
      "utc": "2026-02-26T21:00:00.000Z",
      "local": "Fri, Feb 27, 2026, 24:00",
      "relative": "in 42 days"
    },
    {
      "iso": "2026-03-06T00:00:00+03:00",
      "utc": "2026-03-05T21:00:00.000Z",
      "local": "Fri, Mar 6, 2026, 24:00",
      "relative": "in 49 days"
    },
    {
      "iso": "2026-03-13T00:00:00+03:00",
      "utc": "2026-03-12T21:00:00.000Z",
      "local": "Fri, Mar 13, 2026, 24:00",
      "relative": "in 56 days"
    },
    {
      "iso": "2026-03-20T00:00:00+03:00",
      "utc": "2026-03-19T21:00:00.000Z",
      "local": "Fri, Mar 20, 2026, 24:00",
      "relative": "in 63 days"
    }
  ]
}
css_clampFluid CSS clamp()

Build a fluid typography/spacing value: CSS clamp() that grows linearly from a size at a small viewport to a size at a large viewport (sizes and viewports in px), expressed in rem or px. Also returns the computed size at sample viewport widths. Web version: https://s3eidat.github.io/css-units/

InputTypeDescription
minSizenumber
default 16
Size in px at the small viewport. Default 16.
maxSizenumber
default 32
Size in px at the large viewport. Default 32.
minViewportnumber
default 320
Small viewport width in px. Default 320.
maxViewportnumber
default 1280
Large viewport width in px. Default 1280.
unit"rem" | "px"
default "rem"
Output unit. Default rem.
rootFontSizenumber
default 16
Root (html) font size in px, for rem. Default 16.
sampleViewportsnumber[]
default [320,768,1280,1920]
Viewport widths (px) to compute the resulting size at.

Example

{
  "minSize": 16,
  "maxSize": 32,
  "minViewport": 320,
  "maxViewport": 1280
}

Result

{
  "css": "clamp(1rem, 0.6667rem + 1.6667vw, 2rem)"
}
css_unitsCSS unit converter

Convert a CSS length between px, rem, em, %, pt, pc, in, cm, mm, vw, vh, vmin and vmax, using a root font size, parent font size and viewport size (defaults 16px / 16px / 1440×900). Uses the CSS definition of 96px per inch. Returns every unit, rounded to 4 decimals by default. Web version: https://s3eidat.github.io/css-units/

InputTypeDescription
value requirednumberThe length to convert, e.g. 24.
unit"px" | "rem" | "em" | "%" | "pt" | "vw" | "vh" | "vmin" | "vmax" | "pc" | "in" | "cm" | "mm"
default "px"
Unit of "value". Default px.
rootFontSizenumber
default 16
Root (html) font size in px, for rem. Default 16.
parentFontSizenumber
default 16
Parent element font size in px, for em and %. Default 16.
viewportWidthnumber
default 1440
Viewport width in px, for vw/vmin/vmax. Default 1440.
viewportHeightnumber
default 900
Viewport height in px, for vh/vmin/vmax. Default 900.
decimalsinteger
default 4
Decimal places to round to. Default 4.

Example: 1.5rem, 18pt, 1.6667vw…

{
  "value": 24,
  "unit": "px"
}

Result

{
  "px": 24,
  "results": [
    {
      "unit": "px",
      "name": "Pixels",
      "value": 24,
      "css": "24px"
    },
    {
      "unit": "rem",
      "name": "Root em",
      "value": 1.5,
      "css": "1.5rem"
    },
    {
      "unit": "em",
      "name": "Parent em",
      "value": 1.5,
      "css": "1.5em"
    },
    {
      "unit": "%",
      "name": "Percent of parent font",
      "value": 150,
      "css": "150%"
    },
    {
      "unit": "pt",
      "name": "Points",
      "value": 18,
      "css": "18pt"
    },
    {
      "unit": "vw",
      "name": "Viewport width",
      "value": 1.6667,
      "css": "1.6667vw"
    },
    {
      "unit": "vh",
      "name": "Viewport height",
      "value": 2.6667,
      "css": "2.6667vh"
    },
    {
      "unit": "vmin",
      "name": "Viewport min",
      "value": 2.6667,
      "css": "2.6667vmin"
    },
    {
      "unit": "vmax",
      "name": "Viewport max",
      "value": 1.6667,
      "css": "1.6667vmax"
    },
    {
      "unit": "pc",
      "name": "Picas",
      "value": 1.5,
      "css": "1.5pc"
    },
    {
      "unit": "in",
      "name": "Inches",
      "value": 0.25,
      "css": "0.25in"
    },
    {
      "unit": "cm",
      "name": "Centimetres",
      "value": 0.635,
      "css": "0.635cm"
    },
    {
      "unit": "mm",
      "name": "Millimetres",
      "value": 6.35,
      "css": "6.35mm"
    }
  ]
}
gitignore_generateGenerate .gitignore

Build a .gitignore by combining the website's templates for languages, frameworks, mobile, game engines, tools, OSes and editors (node, python, java, android, ios, flutter, reactnative, unity, unreal, go, rust, cpp, dotnet, php, wordpress, rails, django, nextjs, vite, docker, terraform, macos, windows, linux, vscode, jetbrains, vim). Templates can be named by id, name or keyword (e.g. "javascript" → node). Duplicate patterns are removed, optional "### Name ###" section headers, plus your own custom lines at the end. Returns the text, or writes it to "output" (e.g. "./.gitignore"). Set "list" to see all templates. Web version: https://s3eidat.github.io/gitignore-gen/

InputTypeDescription
templatesstring[]
default []
Templates to combine, in order, e.g. ["node", "nextjs", "vscode", "macos"].
customstringExtra lines to append in a "Custom" section.
headersboolean
default true
Add a generated-by header, section headers and template comments. Default true.
dedupeboolean
default true
Drop patterns that already appeared in an earlier section. Default true.
listboolean
default false
Just list the available templates (id, name, group, keywords).
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).

Example

{
  "templates": [
    "node",
    "nextjs",
    "vscode",
    "macos"
  ],
  "custom": "secrets/"
}

Result

{
  "templates": [
    "node",
    "nextjs",
    "vscode",
    "macos"
  ],
  "patterns": 54,
  "duplicatesRemoved": 2,
  "text": "# Generated with Gitignore Gen (https://s3eidat.github.io/gitignore-gen/)\n# Templates: Node.js, Next.js, VS Code, macOS, Custom\n\n### Node.js ###\n# Dependencies\nnode_modules/\njspm_packages/\n.pnp.*\n.yarn/*\n!.yarn/patches\n!.yarn/plugins\n!.yarn/releases\n!.yarn/versions\n\n# Logs\nlogs/\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\n\n# Build output and caches\ndist/\nbuild/\ncoverage/\n.nyc_output/\n.cache/\n.eslintcache\n.stylelintcache\n*.tsbuildinfo\n.parcel-cache/\n.turbo/\n\n# Environment\n.env\n.env.*\n!.env.example\n\n### Next.js ###\n# Next.js\n/.next/\n/out/\nnext-env.d.ts\n\n# Production\n/bui…"
}

Example

{
  "list": true
}

Result

{
  "available": [
    {
      "id": "node",
      "name": "Node.js",
      "group": "Language",
      "keywords": "npm yarn pnpm javascript typescript bun"
    },
    {
      "id": "python",
      "name": "Python",
      "group": "Language",
      "keywords": "pip venv pytest jupyter poetry conda"
    },
    {
      "id": "java",
      "name": "Java / Gradle / Maven",
      "group": "Language",
      "keywords": "kotlin jvm gradle maven spring"
    },
    {
      "id": "android",
      "name": "Android",
      "group": "Mobile",
      "keywords": "kotlin gradle apk studio"
    },
    {
      "id": "ios",
      "name": "iOS / Xcode",
      "group": "Mobile",
      "keywords": "swift xcode apple cocoapods carthage spm"
    },
    {
      "id": "flutter",
      "name": "Flutter / Dart",
      "group": "Mobile",
      "keywords": "dart pub"
    },
    {
      "id": "reactnative",
      "name": "React Native",
      "group": "Mobile",
      "keywords": "expo metro"
    },
    {
      "id": "unity",
      "name": "Unity",
      "group": "Game",
      "keywords": "game engine csharp"
    },
    {
      "id": "unreal",
      "name": "Unreal Engine",
      "group": "Game",
      "keywords": "game engine cpp ue5"
    },
    {
      "id": "go",
      "name": "Go",
      "group": "Language",
      "keywords": "golang"
    },
    {
      "id": "rust",
      "name": "Rust",
      "group": "Language",
      "keywords": "cargo"
    },
    {
      "id": "cpp",
      "name": "C / C++",
      "group": "Language",
      "keywords": "c cmake make clang gcc"
    },
    {
      "id": "dotnet",
      "name": ".NET / C#",
      "group": "Language",
      "keywords": "csharp visual studio nuget aspnet"
    },
    {
      "id": "php",
      "name": "PHP / Laravel",
      "group": "Language",
      "keywords": "composer laravel symfony"
    },
    {
      "id": "wordpress",
      "name": "WordPress",
      "group": "Framework",
      "keywords": "wp cms php"
    },
    {
      "id": "rails",
      "name": "Ruby / Rails",
      "group": "Framework",
      "keywords": "ruby bundler gem"
    },
    {
      "id": "django",
      "name": "Django",
      "group": "Framework",
      "keywords": "python web"
    },
    {
      "id": "nextjs",
      "name": "Next.js",
      "group": "Framework",
      "keywords": "react vercel node"
    },
    {
      "id": "vite",
      "name": "Vite",
      "group": "Framework",
      "keywords": "react vue svelte frontend"
    },
    {
      "id": "docker",
      "name": "Docker",
      "group": "Tool",
      "keywords": "container compose"
    },
    {
      "id": "terraform",
      "name": "Terraform",
      "group": "Tool",
      "keywords": "hcl iac infrastructure"
    },
    {
      "id": "macos",
      "name": "macOS",
      "group": "OS",
      "keywords": "mac apple finder"
    },
    {
      "id": "windows",
      "name": "Windows",
      "group": "OS",
      "keywords": "explorer microsoft"
    },
    {
      "id": "linux",
      "name": "Linux",
      "group": "OS",
      "keywords": "ubuntu unix"
    },
    {
      "id": "vscode",
      "name": "VS Code",
      "group": "Editor",
      "keywords": "visual studio code editor"
    },
    {
      "id": "jetbrains",
      "name": "JetBrains",
      "group": "Editor",
      "keywords": "intellij idea webstorm pycharm android studio rider"
    },
    {
      "id": "vim",
      "name": "Vim",
      "group": "Editor",
      "keywords": "neovim nvim"
    }
  ]
}
hash_computeHash (MD5 / SHA)

Compute MD5, SHA-1, SHA-256 and SHA-512 of text or a file (path or base64), optionally an HMAC with a key, and check against an expected checksum (hex in any case/spacing, "SHA256 (file) = …" lines, or base64). Web version: https://s3eidat.github.io/hash-lab/

InputTypeDescription
textstringText to hash (UTF-8).
filestringOr: a file to hash: local path or base64.
hmacKeystringIf set, also compute HMAC with this key.
hmacAlgorithm"SHA-1" | "SHA-256" | "SHA-384" | "SHA-512"
default "SHA-256"
format"hex" | "HEX" | "base64"
default "hex"
Output encoding.
expectedstringA checksum to compare against; the result says which algorithm matched.

Example: SHA-256 = ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad

{
  "text": "abc"
}

Result

{
  "bytes": 3,
  "hashes": {
    "MD5": "900150983cd24fb0d6963f7d28e17f72",
    "SHA-1": "a9993e364706816aba3e25717850c26c9cd0d89d",
    "SHA-256": "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
    "SHA-512": "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"
  }
}
http_statusHTTP status codes

Look up HTTP status codes: by exact code (e.g. 418, also non-standard ones like 499 or 524), by code prefix ("50"), or by words ("too many", "redirect post"), optionally limited to a class (1xx–5xx). Each result has the name, what it means, when to use it, common causes and the defining spec. Non-standard nginx/Cloudflare codes are only searched when "includeNonStandard" is set. Web version: https://s3eidat.github.io/http-status/

InputTypeDescription
querystring
default ""
A code ("404"), code prefix ("5", "50") or search words. Empty = all codes (filtered by class).
class"1" | "2" | "3" | "4" | "5"Only this class: 1 informational, 2 success, 3 redirection, 4 client error, 5 server error.
includeNonStandardboolean
default false
Also search non-standard codes (nginx 499, Cloudflare 52x…).
limitinteger
default 20
Max results to return. Default 20.

Example: 429 Too Many Requests with meaning, when to use and causes

{
  "query": "429"
}

Result

{
  "count": 1,
  "results": [
    {
      "code": 429,
      "name": "Too Many Requests",
      "class": "4xx",
      "className": "Client error",
      "meaning": "You sent too many requests in a given time (rate limiting).",
      "whenToUse": "Rate limits. Send Retry-After to say when to try again.",
      "commonCauses": "Scripts and bots looping fast, missing back-off, shared IPs.",
      "spec": "RFC 6585",
      "nonStandard": false
    }
  ]
}

Example

{
  "query": "teapot"
}

Result

{
  "count": 1,
  "results": [
    {
      "code": 418,
      "name": "I'm a teapot",
      "class": "4xx",
      "className": "Client error",
      "meaning": "An April Fools' joke: the server refuses to brew coffee because it is a teapot.",
      "whenToUse": "Easter eggs. Some sites use it to answer requests they want to refuse.",
      "commonCauses": "Hyper Text Coffee Pot Control Protocol jokes, bot blocking.",
      "spec": "RFC 2324",
      "nonStandard": false
    }
  ]
}
id_generateGenerate IDs (UUID / ULID / NanoID)

Generate 1–1000 unique IDs with a cryptographic RNG: UUID v4 (random), UUID v7 (time-ordered, RFC 9562, strictly increasing within a batch), ULID (26 Crockford Base32 chars, monotonic) or NanoID (custom length 2–128 and alphabet: URL-safe default, alphanumeric, hex, digits, no look-alikes, or your own). Options: uppercase, no dashes. Web version: https://s3eidat.github.io/uuid-forge/

InputTypeDescription
kind"v4" | "v7" | "ulid" | "nanoid"
default "v4"
Which ID type.
countinteger
default 1
How many IDs (1–1000).
uppercaseboolean
default false
Upper-case output (UUID hex digits, ULID letters). Default false = lower case, like the website.
noDashesboolean
default false
UUIDs only: drop the dashes (32 hex chars).
lengthinteger
default 21
NanoID length (2–128). 21 URL-safe chars ≈ UUID v4 collision safety.
alphabetstring
default "A-Za-z0-9_-"
NanoID alphabet: a preset ("A-Za-z0-9_-", "A-Za-z0-9", "0-9a-f", "0-9", "nolook") or your own characters (2–256 distinct; duplicates ignored).

Example: Three time-ordered UUIDs, e.g. 019a2f3c-8d41-7a0b-9c2e-5f1d3b7a4c10

{
  "kind": "v7",
  "count": 3
}

Example

{
  "kind": "nanoid",
  "length": 12,
  "alphabet": "nolook"
}
id_inspectInspect a UUID / ULID

Check whether a string is a valid UUID (any version; braces, urn:uuid: and missing dashes accepted) or ULID, and report its version, variant and canonical form. For UUID v1, v6, v7 and ULID also returns the embedded creation time. Web version: https://s3eidat.github.io/uuid-forge/

InputTypeDescription
id requiredstringThe UUID or ULID to inspect.

Example

{
  "id": "017F22E2-79B0-7CC3-98C4-DC0C0C07398F"
}

Result

{
  "valid": true,
  "kind": "UUID",
  "version": 7,
  "variant": "RFC 9562 / 4122",
  "canonical": "017f22e2-79b0-7cc3-98c4-dc0c0c07398f",
  "createdAt": "2022-02-22T19:22:22.000Z",
  "timestampMs": 1645557742000
}
ip_in_rangeIs this IP in that range?

Check whether an IPv4 or IPv6 address falls inside a range given as CIDR ("10.0.0.0/8", "2001:db8::/32"), a dash range ("192.168.1.10 - 192.168.1.60") or a single address. Web version: https://s3eidat.github.io/subnet-calc/

InputTypeDescription
ip requiredstringThe address to test.
range requiredstringCIDR, "start - end" range, or single address (same family as "ip").

Example

{
  "ip": "192.168.10.77",
  "range": "192.168.10.64/27"
}

Result

{
  "inside": true,
  "rangeStart": "192.168.10.64",
  "rangeEnd": "192.168.10.95"
}
jwt_decodeJWT decode

Decode a JSON Web Token (a leading "Bearer " is fine): header, payload, iat/nbf/exp/auth_time as ISO dates with relative time, expired / not-yet-valid status, and named standard claims. If "secret" is given, verifies HS256/HS384/HS512 signatures (secret as text or Base64URL). RS/ES/PS tokens are decoded but not verified (needs a public key). Encrypted JWE tokens can't be read. Web version: https://s3eidat.github.io/jwt-inspector/

InputTypeDescription
token requiredstringThe JWT (header.payload.signature).
secretstringHMAC secret to verify an HS256/384/512 signature.
secretIsBase64boolean
default false
The secret is Base64/Base64URL-encoded bytes, not text.
nownumberUnix time in seconds to judge expiry against. Default: now.

Example: payload.name = "John Doe", signature.verified = true, status "No expiry (exp) claim"

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
  "secret": "your-256-bit-secret"
}

Result

{
  "header": {
    "alg": "HS256",
    "typ": "JWT"
  },
  "payload": {
    "sub": "1234567890",
    "name": "John Doe",
    "iat": 1516239022
  },
  "algorithm": "HS256",
  "unsigned": false,
  "status": "warn",
  "statusText": "No expiry (exp) claim",
  "expired": false,
  "times": [
    {
      "claim": "iat",
      "label": "Issued at",
      "value": 1516239022,
      "iso": "2018-01-18T01:30:22.000Z",
      "relative": "8 years ago"
    }
  ],
  "claims": [
    {
      "claim": "sub",
      "label": "Subject",
      "value": "1234567890"
    },
    {
      "claim": "name",
      "label": "Name",
      "value": "John Doe"
    }
  ],
  "signature": {
    "verified": true,
    "message": "Signature verified"
  }
}
meta_tags_generateMeta tags generator

Generate the <head> HTML for SEO and social sharing: <title>, meta description, canonical link, Open Graph (og:type, url, title, description, image + alt, site_name, locale) and X/Twitter card tags. All values are HTML-escaped. Also checks lengths (title 30–60, description 70–160 characters), URLs and the @handle, and shows how Google, Facebook and X would truncate the title and description. URLs without a scheme get https://. Optionally writes the tags to an .html file. Web version: https://s3eidat.github.io/meta-tags/

InputTypeDescription
titlestring
default ""
Page title (aim for 30–60 characters).
descriptionstring
default ""
Meta description (aim for 70–160 characters).
urlstring
default ""
Canonical page URL.
imagestring
default ""
Share image URL (1200×630 recommended).
imageAltstring
default ""
Alt text for the share image.
siteNamestring
default ""
Site name (og:site_name).
twitterstring
default ""
X/Twitter handle: "@name", "name" or a profile URL.
typestring
default "website"
og:type, e.g. website, article, product, profile. Default website.
card"summary_large_image" | "summary"
default "summary_large_image"
X card type. Default summary_large_image.
localestring
default ""
og:locale, e.g. en_US or ar_AR.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).

Example

{
  "title": "Meta Tags: free SEO and social preview generator",
  "description": "Create meta, Open Graph and X card tags with live Google, Facebook and X previews. Free, private and it runs in your browser.",
  "url": "https://s3eidat.github.io/meta-tags/",
  "image": "https://s3eidat.github.io/meta-tags/screenshot.png",
  "siteName": "S3eidat Tools",
  "twitter": "@s3eidat",
  "locale": "en_US"
}

Result

{
  "html": "<title>Meta Tags: free SEO and social preview generator</title>\n<meta name=\"description\" content=\"Create meta, Open Graph and X card tags with live Google, Facebook and X previews. Free, private and it runs in your browser.\">\n<link rel=\"canonical\" href=\"https://s3eidat.github.io/meta-tags/\">\n\n<!-- Open Graph (Facebook, LinkedIn, WhatsApp, Slack…) -->\n<meta property=\"og:type\" content=\"website\">\n<meta property=\"og:url\" content=\"https://s3eidat.github.io/meta-tags/\">\n<meta property=\"og:title\" content=\"Meta Tags: free SEO and social preview generator\">\n<meta property=\"og:description\" content=\"Crea…",
  "problems": 0,
  "checks": {
    "title": {
      "level": "ok",
      "message": "48 / 60"
    },
    "description": {
      "level": "ok",
      "message": "125 / 160"
    },
    "url": {
      "level": "ok",
      "message": "valid"
    },
    "image": {
      "level": "ok",
      "message": "valid"
    },
    "twitter": {
      "level": "ok",
      "message": "@s3eidat"
    }
  },
  "previews": {
    "google": {
      "site": "S3eidat Tools",
      "breadcrumb": "s3eidat.github.io › meta-tags",
      "title": "Meta Tags: free SEO and social preview generator",
      "description": "Create meta, Open Graph and X card tags with live Google, Facebook and X previews. Free, private and it runs in your browser."
    },
    "facebook": {
      "domain": "S3EIDAT.GITHUB.IO",
      "title": "Meta Tags: free SEO and social preview generator",
      "description": "Create meta, Open Graph and X card tags with live Google, Facebook and X previews. Free, private and it runs…"
    },
    "x": {
      "card": "summary_large_image",
      "domain": "s3eidat.github.io",
      "title": "Meta Tags: free SEO and social preview generator",
      "description": "Create meta, Open Graph and X card tags with live Google, Facebook and X previews. Free, private and it runs in your browser."
    }
  }
}
All tools · S3eidat Tools MCP
,
, $). flags g i m s u y (and d, v). runs in an isolated worker with a timeout (default 1.5 s) so catastrophic backtracking is reported instead of hanging. lists up to 5000 matches. web version: https://s3eidat.github.io/regex-playground/">regex_testRegex test

Run a JavaScript regular expression against text: every match with its index, length, numbered and named capture groups, plus an optional replace preview ($1, {{body}}lt;name>, {{body}}amp;, All tools · S3eidat Tools MCP

,
, $). Flags g i m s u y (and d, v). Runs in an isolated worker with a timeout (default 1.5 s) so catastrophic backtracking is reported instead of hanging. Lists up to 5000 matches. Web version: https://s3eidat.github.io/regex-playground/

InputTypeDescription
pattern requiredstringThe regex source, without slashes, e.g. "(\\d+)-(?<w>[a-z]+)".
flagsstring
default "g"
Regex flags, default "g".
text requiredstringThe text to search.
replacestringReplacement string for a replace preview (JavaScript String.replace syntax).
limitinteger
default 500
Max matches to include in the result (count is still total). Default 500.
timeoutMsinteger
default 1500
Kill the regex after this many ms. Default 1500.

Example: 2 matches (index 0 and 5), replaced = "ab:1 cd:22 x"

{
  "pattern": "(\\d+)-(?<w>[a-z]+)",
  "flags": "g",
  "text": "1-ab 22-cd x",
  "replace": "{{body}}lt;w>:$1"
}

Result

{
  "count": 2,
  "more": false,
  "groupNames": [
    null,
    "w"
  ],
  "matches": [
    {
      "index": 0,
      "length": 4,
      "text": "1-ab",
      "groups": [
        "1",
        "ab"
      ],
      "named": {
        "w": "ab"
      }
    },
    {
      "index": 5,
      "length": 5,
      "text": "22-cd",
      "groups": [
        "22",
        "cd"
      ],
      "named": {
        "w": "cd"
      }
    }
  ],
  "replaced": "ab:1 cd:22 x"
}
subnet_calcSubnet calculator (IPv4 / IPv6)

IPv4: from "192.168.1.0/26", "10.0.0.5 255.255.255.0" or a wildcard mask, get network, broadcast, first/last host, usable and total addresses, netmask, wildcard, class, address type (private, CGNAT, loopback, documentation…), integer/hex/binary forms; optionally split into N equal subnets or allocate VLSM blocks by host counts ("Sales:50, Ops:20"). /31 and /32 follow RFC 3021. IPv6: compressed (RFC 5952) and expanded forms, network, first/last address, address count, number of /64s, reverse DNS name and type. Web version: https://s3eidat.github.io/subnet-calc/

InputTypeDescription
address requiredstringIPv4 or IPv6 address, optionally with /prefix (or an IPv4 dotted mask after a space or slash).
prefixintegerPrefix length to use when "address" has none (default /24 for IPv4, /128 for IPv6), or to override it.
splitintegerIPv4: split the network into at least this many equal subnets (rounded up to a power of two; first 4096 listed).
vlsmstringIPv4: VLSM host requirements, e.g. "50, 20, 10" or "Sales:50, Ops:20". Largest first, each block aligned.

Example: broadcast 192.168.1.63, hosts 192.168.1.1–192.168.1.62, 62 usable, mask 255.255.255.192

{
  "address": "192.168.1.0/26"
}

Result

{
  "version": 4,
  "address": "192.168.1.0",
  "prefix": 26,
  "network": "192.168.1.0/26",
  "broadcast": "192.168.1.63",
  "firstHost": "192.168.1.1",
  "lastHost": "192.168.1.62",
  "usableHosts": 62,
  "totalAddresses": 64,
  "subnetMask": "255.255.255.192",
  "wildcardMask": "0.0.0.63",
  "class": "C",
  "type": "Private (RFC 1918)",
  "isPublic": false,
  "integer": 3232235776,
  "hex": "0xC0A80100",
  "binary": {
    "address": "11000000.10101000.00000001.00000000",
    "mask": "11111111.11111111.11111111.11000000",
    "network": "11000000.10101000.00000001.00000000",
    "broadcast": "11000000.10101000.00000001.00111111"
  },
  "summary": "Address:      192.168.1.0/26\nNetwork:      192.168.1.0/26\nNetmask:      255.255.255.192\nWildcard:     0.0.0.63\nBroadcast:    192.168.1.63\nHost range:   192.168.1.1 - 192.168.1.62\nUsable hosts: 62\nTotal:        64\nType:         Private (RFC 1918)"
}

Example: 192.168.1.0/26, .64/27, .96/28, .112/30

{
  "address": "192.168.1.0/24",
  "vlsm": "2, 50, 10, 20"
}

Result

{
  "version": 4,
  "address": "192.168.1.0",
  "prefix": 24,
  "network": "192.168.1.0/24",
  "broadcast": "192.168.1.255",
  "firstHost": "192.168.1.1",
  "lastHost": "192.168.1.254",
  "usableHosts": 254,
  "totalAddresses": 256,
  "subnetMask": "255.255.255.0",
  "wildcardMask": "0.0.0.255",
  "class": "C",
  "type": "Private (RFC 1918)",
  "isPublic": false,
  "integer": 3232235776,
  "hex": "0xC0A80100",
  "binary": {
    "address": "11000000.10101000.00000001.00000000",
    "mask": "11111111.11111111.11111111.00000000",
    "network": "11000000.10101000.00000001.00000000",
    "broadcast": "11000000.10101000.00000001.11111111"
  },
  "summary": "Address:      192.168.1.0/24\nNetwork:      192.168.1.0/24\nNetmask:      255.255.255.0\nWildcard:     0.0.0.255\nBroadcast:    192.168.1.255\nHost range:   192.168.1.1 - 192.168.1.254\nUsable hosts: 254\nTotal:        256\nType:         Private (RFC 1918)",
  "vlsm": {
    "used": 116,
    "free": 140,
    "subnets": [
      {
        "name": "Subnet 2",
        "needed": 50,
        "network": "192.168.1.0/26",
        "firstHost": "192.168.1.1",
        "lastHost": "192.168.1.62",
        "broadcast": "192.168.1.63",
        "usableHosts": 62,
        "mask": "255.255.255.192"
      },
      {
        "name": "Subnet 4",
        "needed": 20,
        "network": "192.168.1.64/27",
        "firstHost": "192.168.1.65",
        "lastHost": "192.168.1.94",
        "broadcast": "192.168.1.95",
        "usableHosts": 30,
        "mask": "255.255.255.224"
      },
      {
        "name": "Subnet 3",
        "needed": 10,
        "network": "192.168.1.96/28",
        "firstHost": "192.168.1.97",
        "lastHost": "192.168.1.110",
        "broadcast": "192.168.1.111",
        "usableHosts": 14,
        "mask": "255.255.255.240"
      },
      {
        "name": "Subnet 1",
        "needed": 2,
        "network": "192.168.1.112/30",
        "firstHost": "192.168.1.113",
        "lastHost": "192.168.1.114",
        "broadcast": "192.168.1.115",
        "usableHosts": 2,
        "mask": "255.255.255.252"
      }
    ]
  }
}

Example

{
  "address": "2001:db8:abcd::1/48"
}

Result

{
  "version": 6,
  "type": "Documentation",
  "compressed": "2001:db8:abcd::1",
  "expanded": "2001:0db8:abcd:0000:0000:0000:0000:0001",
  "prefix": 48,
  "network": "2001:db8:abcd::/48",
  "firstAddress": "2001:0db8:abcd:0000:0000:0000:0000:0000",
  "lastAddress": "2001:0db8:abcd:ffff:ffff:ffff:ffff:ffff",
  "addresses": "1,208,925,819,614,629,174,706,176 (2^80)",
  "subnets64": "65,536 (2^16)",
  "reverseDns": "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.c.b.a.8.b.d.0.1.0.0.2.ip6.arpa"
}
time_convertTimestamp convert

Convert a Unix timestamp (seconds, ms, µs or ns — auto-detected from the number of digits, or set "unit") or a date string to ISO 8601 (UTC and in a timezone), RFC 7231 / UTC string, local time in any IANA timezone with its offset, relative time ("3 hours ago"), day of year, ISO week, and Unix seconds/ms. A wall-clock time without an offset (e.g. "2026-10-26T14:30") is read in "timezone". Also shows the same instant in extra "zones" (world clock). No value = now. Date arithmetic is not done here. Web version: https://s3eidat.github.io/timestamp-convert/

InputTypeDescription
valuestring | numberA Unix timestamp (e.g. 1700000000 or "1700000000123"), an ISO/RFC date string, or a wall-clock "YYYY-MM-DDTHH:MM[:SS]". Default: now.
unit"auto" | "s" | "ms" | "us" | "ns"
default "auto"
Unit of a numeric timestamp. Default: auto-detect.
timezonestringIANA timezone for the local view and for wall-clock input. Default: the server's (Asia/Amman).
zonesstring[]Extra IANA timezones to show the same instant in, e.g. ["UTC","Asia/Tokyo"].
nownumberReference "now" in Unix ms for the relative time. Default: the current time.

Example: iso 2023-11-14T22:13:20.000Z, localIso 2023-11-15T01:13:20+03:00, detectedUnit seconds

{
  "value": 1700000000,
  "timezone": "Asia/Amman"
}

Result

{
  "input": "1700000000",
  "unixSeconds": 1700000000,
  "unixMilliseconds": 1700000000000,
  "iso": "2023-11-14T22:13:20.000Z",
  "utc": "Tue, 14 Nov 2023 22:13:20 GMT",
  "timezone": "Asia/Amman",
  "local": "Wed, Nov 15, 2023, 01:13:20 AM",
  "localIso": "2023-11-15T01:13:20+03:00",
  "offset": "UTC+03:00",
  "relative": "2 years ago",
  "dayOfYear": 319,
  "isoWeek": 46,
  "detectedUnit": "seconds"
}

Example: unixSeconds 1782864000 (2026-07-01T00:00:00Z)

{
  "value": "2026-07-01T09:00",
  "timezone": "Asia/Tokyo"
}

Result

{
  "input": "2026-07-01T09:00",
  "unixSeconds": 1782864000,
  "unixMilliseconds": 1782864000000,
  "iso": "2026-07-01T00:00:00.000Z",
  "utc": "Wed, 01 Jul 2026 00:00:00 GMT",
  "timezone": "Asia/Tokyo",
  "local": "Wed, Jul 1, 2026, 09:00:00 AM",
  "localIso": "2026-07-01T09:00:00+09:00",
  "offset": "UTC+09:00",
  "relative": "in 5 months",
  "dayOfYear": 182,
  "isoWeek": 27
}
url_decodeURL decode

Decode percent-encoded text (decodeURIComponent, or decodeURI with mode "full"). Optionally turn "+" into spaces (form encoding). Errors on malformed %-sequences. Web version: https://s3eidat.github.io/url-toolkit/

InputTypeDescription
text requiredstringPercent-encoded text.
mode"component" | "full"
default "component"
"component" = encodeURIComponent (for one query value or path segment; escapes / ? & = #). "full" = encodeURI (a whole URL; keeps those).
plusAsSpaceboolean
default false
Treat "+" as a space (application/x-www-form-urlencoded).

Example

{
  "text": "x+y%20%E2%9C%93",
  "plusAsSpace": true
}

Result

{
  "result": "x y ✓"
}
url_encodeURL encode

Percent-encode text for URLs, exactly like JavaScript encodeURIComponent ("component", default) or encodeURI ("full"). UTF-8 based. Web version: https://s3eidat.github.io/url-toolkit/

InputTypeDescription
text requiredstringText to encode.
mode"component" | "full"
default "component"
"component" = encodeURIComponent (for one query value or path segment; escapes / ? & = #). "full" = encodeURI (a whole URL; keeps those).

Example

{
  "text": "a b&c/d?é"
}

Result

{
  "result": "a%20b%26c%2Fd%3F%C3%A9"
}
url_parseURL parse

Split a URL into protocol, user, host, port, path, query, fragment and origin (with decoded forms), list its query parameters in order (duplicates and bare flags kept, + as space) and flag tracking parameters (utm_*, fbclid, gclid, msclkid, mc_cid, _ga, _hsenc…). Optionally returns the URL with tracking removed. A missing scheme means https://. Passwords are never echoed. Web version: https://s3eidat.github.io/url-toolkit/

InputTypeDescription
url requiredstringThe URL to parse.
stripTrackingboolean
default false
Also return "cleanUrl" with tracking parameters removed.

Example: cleanUrl = https://example.com/shop/item?id=42#reviews, removed = 2

{
  "url": "https://example.com/shop/item?id=42&utm_source=newsletter&fbclid=IwAR0xYz#reviews",
  "stripTracking": true
}

Result

{
  "parts": {
    "href": "https://example.com/shop/item?id=42&utm_source=newsletter&fbclid=IwAR0xYz#reviews",
    "protocol": "https",
    "username": "",
    "hasPassword": false,
    "host": "example.com",
    "port": "",
    "defaultPort": "443",
    "path": "/shop/item",
    "query": "id=42&utm_source=newsletter&fbclid=IwAR0xYz",
    "fragment": "reviews",
    "origin": "https://example.com"
  },
  "params": [
    {
      "key": "id",
      "value": "42",
      "tracking": false
    },
    {
      "key": "utm_source",
      "value": "newsletter",
      "tracking": true
    },
    {
      "key": "fbclid",
      "value": "IwAR0xYz",
      "tracking": true
    }
  ],
  "trackingCount": 2,
  "cleanUrl": "https://example.com/shop/item?id=42#reviews",
  "removed": 2
}
useragent_parseUser agent parse

Parse browser user-agent strings into browser + version, engine (Blink, WebKit, Gecko, Trident, EdgeHTML, Presto), OS + version, device type (desktop, mobile, tablet, TV, console, bot, HTTP client), vendor and model. Recognises ~50 crawlers and AI bots (Googlebot, Bingbot, GPTBot, ClaudeBot, PerplexityBot…) and HTTP clients (curl, python-requests, Go, okhttp, Postman…). Give one "userAgent", a list, or a text/log file (one per line, or the last quoted field of each access-log line); up to 5000 per call, with a count by type. Web version: https://s3eidat.github.io/ua-parser/

InputTypeDescription
userAgentstringOne user-agent string.
userAgentsstring[]Several user-agent strings.
filestringOr: a text file (path or base64) with one user agent per line, or an access log.
accessLogboolean
default false
Read "file" as an access log: take the last quoted field of each line.

Example: Mobile Safari 17.4, WebKit, iOS 17.4, mobile, Apple iPhone

{
  "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Mobile/15E148 Safari/604.1"
}

Result

{
  "count": 1,
  "byType": {
    "mobile": 1
  },
  "results": [
    {
      "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Mobile/15E148 Safari/604.1",
      "browser": "Mobile Safari",
      "browserVersion": "17.4",
      "engine": "WebKit",
      "engineVersion": "605.1.15",
      "os": "iOS",
      "osVersion": "17.4",
      "type": "mobile",
      "vendor": "Apple",
      "model": "iPhone",
      "bot": "",
      "client": "",
      "typeLabel": "Mobile",
      "summary": {
        "browser": "Mobile Safari 17.4",
        "engine": "WebKit 605.1.15",
        "os": "iOS 17.4",
        "device": "Apple iPhone"
      }
    }
  ]
}

Calculators

Loans, interest, percentages, salary, GPA, zakat, BMI, dates, units, Hijri, maths.

calc_ageAge calculator

Exact age in years, months and days from a date of birth (on today or any "on" date), with totals in months, weeks, days and hours, the weekday of birth, Hijri (Umm al-Qura) birth date and Hijri age, next birthday (Feb 29 birthdays fall on Feb 28 in common years), star sign, Chinese zodiac, the next 1,000-day milestone, and optionally the age gap to a second person. Dates are ISO YYYY-MM-DD. Web version: https://s3eidat.github.io/age-calc/

InputTypeDescription
birthDate requiredstringDate of birth, YYYY-MM-DD.
onDatestringWork out the age on this date (YYYY-MM-DD). Default: today.
otherBirthDatestringOptional second person's date of birth, to compare.
otherNamestringName of the second person (default "They").

Example: 32 years, 5 months, 11 days

{
  "birthDate": "1994-05-15",
  "onDate": "2026-10-26"
}

Result

{
  "years": 32,
  "months": 5,
  "days": 11,
  "text": "32 years, 5 months, 11 days",
  "totalMonths": 389,
  "totalWeeks": 1693,
  "weeksRemainderDays": 1,
  "totalDays": 11852,
  "approxHours": 284448,
  "bornOn": {
    "date": "1994-05-15",
    "weekday": "Sunday",
    "long": "15 May 1994"
  },
  "hijri": {
    "birth": "Dhuʻl-Hijjah 4, 1414 AH",
    "birthArabic": "٤ ذو الحجة ١٤١٤ هـ",
    "age": 33
  },
  "birthdayToday": false,
  "nextBirthday": {
    "date": "2027-05-15",
    "weekday": "Saturday",
    "inDays": 201,
    "turning": 33
  },
  "starSign": "Taurus ♉",
  "chineseZodiac": "Dog",
  "nextMilestone": {
    "days": 12000,
    "date": "2027-03-23",
    "weekday": "Tuesday",
    "inDays": 148
  },
  "summary": "Age: 32 years, 5 months, 11 days (on 26 October 2026)\nBorn: Sunday, 15 May 1994 (Dhuʻl-Hijjah 4, 1414 AH)\nTotal: 11,852 days, 1,693 weeks\nNext birthday: 15 May 2027 (Saturday), in 201 days"
}
calc_bmiBMI calculator

Body mass index with the WHO adult category, the healthy weight range for your height (BMI 18.5–24.9), BMI Prime, optional waist-to-height ratio, and for adults (18–120, with age) BMR by Mifflin-St Jeor plus daily calories to maintain or lose ~0.5 kg (1 lb) a week at five activity levels. Metric (cm, kg) or imperial (ft + in, lb). Height 50–272 cm, weight 10–650 kg. Not medical advice; children need percentile charts. Web version: https://s3eidat.github.io/bmi-calc/

InputTypeDescription
units"metric" | "imperial"
default "metric"
metric: cm and kg; imperial: ft + in and lb.
heightCmnumber | stringHeight in cm (metric).
heightFtnumber | stringHeight, feet part (imperial).
heightInnumber | stringHeight, inches part (imperial).
weightnumber | stringWeight in kg (metric) or lb (imperial).
agenumber | stringAge in years (adults 18+ get BMR and calories).
sex"m" | "f"
default "m"
Sex, for BMR (m or f).
waistnumber | stringOptional waist size in cm (metric) or inches (imperial).

Example: BMI 22.9 Healthy weight, BMR 1649 kcal

{
  "heightCm": 175,
  "weight": 70,
  "age": 30,
  "sex": "m"
}

Result

{
  "bmi": 22.9,
  "category": "Healthy weight",
  "categoryClass": 1,
  "healthyRange": {
    "min": 56.7,
    "max": 76.3,
    "unit": "kg"
  },
  "toHealthyRange": "you are in range",
  "bmiPrime": 0.91,
  "bmr": 1649,
  "calories": [
    {
      "activity": "Sedentary",
      "factor": 1.2,
      "maintain": 1979,
      "lose": 1500
    },
    {
      "activity": "Light",
      "factor": 1.375,
      "maintain": 2267,
      "lose": 1767
    },
    {
      "activity": "Moderate",
      "factor": 1.55,
      "maintain": 2556,
      "lose": 2056
    },
    {
      "activity": "Active",
      "factor": 1.725,
      "maintain": 2844,
      "lose": 2344
    },
    {
      "activity": "Very active",
      "factor": 1.9,
      "maintain": 3133,
      "lose": 2633
    }
  ],
  "summary": "BMI 22.9 (Healthy weight)\nHealthy weight for 175 cm: 56.7 kg – 76.3 kg\nBMR: 1,649 kcal/day"
}
calc_cash_countCash counter

Count a cash drawer by denomination with exact integer maths (no float errors): JOD (3 decimals, fils), USD, EUR, SAR, AED, GBP, KWD, EGP, or a custom currency (your own note and coin values, 0/2/3 decimals). Give how many of each note/coin (by value like 50 or 0.25, by label like "50 JD" or "¼ dinar"); returns per-denomination amounts, the total, notes and coins counted, and over/short against an expected amount. list=true shows the denominations of a currency. Web version: https://s3eidat.github.io/cash-counter/

InputTypeDescription
currency"JOD" | "USD" | "EUR" | "SAR" | "AED" | "GBP" | "KWD" | "EGP" | "CUSTOM"
default "JOD"
Currency preset, or CUSTOM.
customobjectCUSTOM currency definition.
countsobject[]
default []
What you counted.
expectednumber | stringExpected total, to show over/short.
notestringOptional note for the summary (e.g. "Evening shift").
listboolean
default false
Just list the denominations.

Example: total 151.350 JD, short by 0.050

{
  "currency": "JOD",
  "counts": [
    {
      "denomination": 50,
      "count": 3
    },
    {
      "denomination": 0.5,
      "count": 2
    },
    {
      "denomination": 0.05,
      "count": 7
    }
  ],
  "expected": "151.400"
}

Result

{
  "currency": "JOD",
  "name": "Jordanian dinar",
  "symbol": "JD",
  "decimals": 3,
  "denominations": [
    {
      "key": "n50000",
      "label": "50 JD",
      "labelArabic": "٥٠ ديناراً",
      "kind": "note",
      "value": "50.000",
      "count": 3,
      "amount": "150.000"
    },
    {
      "key": "n20000",
      "label": "20 JD",
      "labelArabic": "٢٠ ديناراً",
      "kind": "note",
      "value": "20.000",
      "count": 0,
      "amount": "0.000"
    },
    {
      "key": "n10000",
      "label": "10 JD",
      "labelArabic": "١٠ دنانير",
      "kind": "note",
      "value": "10.000",
      "count": 0,
      "amount": "0.000"
    },
    {
      "key": "n5000",
      "label": "5 JD",
      "labelArabic": "٥ دنانير",
      "kind": "note",
      "value": "5.000",
      "count": 0,
      "amount": "0.000"
    },
    {
      "key": "n1000",
      "label": "1 JD",
      "labelArabic": "دينار",
      "kind": "note",
      "value": "1.000",
      "count": 0,
      "amount": "0.000"
    },
    {
      "key": "c500",
      "label": "½ dinar",
      "labelArabic": "نصف دينار",
      "kind": "coin",
      "value": "0.500",
      "count": 2,
      "amount": "1.000"
    },
    {
      "key": "c250",
      "label": "¼ dinar",
      "labelArabic": "ربع دينار",
      "kind": "coin",
      "value": "0.250",
      "count": 0,
      "amount": "0.000"
    },
    {
      "key": "c100",
      "label": "10 piasters",
      "labelArabic": "١٠ قروش",
      "kind": "coin",
      "value": "0.100",
      "count": 0,
      "amount": "0.000"
    },
    {
      "key": "c50",
      "label": "5 piasters",
      "labelArabic": "٥ قروش",
      "kind": "coin",
      "value": "0.050",
      "count": 7,
      "amount": "0.350"
    },
    {
      "key": "c10",
      "label": "1 piaster",
      "labelArabic": "قرش",
      "kind": "coin",
      "value": "0.010",
      "count": 0,
      "amount": "0.000"
    }
  ],
  "total": "151.350",
  "totalMinorUnits": 151350,
  "notes": 3,
  "coins": 9,
  "expected": {
    "amount": "151.400",
    "status": "short",
    "difference": "0.050"
  },
  "summary": "Cash count · Jordanian dinar (JOD)\n\n50 JD × 3 = 150.000\n½ dinar × 2 = 1.000\n5 piasters × 7 = 0.350\n\nTotal: 151.350 JD (3 notes, 9 coins)\nExpected: 151.400 JD · short by 0.050"
}
calc_compound_interestCompound interest / savings growth

Project savings with compound interest: an initial deposit plus monthly contributions (at the start or end of each month), a nominal annual rate compounded yearly, half-yearly, quarterly, monthly, daily or continuously, optionally adjusted for inflation ("today's money"). Returns final balance, total deposits, interest earned and a year-by-year table. Rate -50–100 %, 1 month to 100 years. Web version: https://s3eidat.github.io/compound-interest/

InputTypeDescription
initialnumber | stringInitial deposit (default 0).
monthlynumber | stringMonthly contribution (default 0).
annualRate requirednumber | stringNominal annual interest rate in percent, e.g. 7.
years requirednumber | stringDuration in years (fractions allowed, e.g. 1.5; at least one month).
compounding"yearly" | "half-yearly" | "quarterly" | "monthly" | "daily" | "continuously"
default "monthly"
How often interest compounds.
timing"end" | "start"
default "end"
Contributions at the end (ordinary annuity) or start (annuity due) of each month.
inflationnumber | stringOptional yearly inflation in percent, to show the balance in today's money.
tableboolean
default true
Include the year-by-year table.

Example: finalBalance 16288.95

{
  "initial": 10000,
  "annualRate": 5,
  "years": 10,
  "compounding": "yearly"
}

Result

{
  "finalBalance": 16288.95,
  "totalDeposits": 10000,
  "interestEarned": 6288.95,
  "growthMultiple": 1.63,
  "rows": [
    {
      "year": 1,
      "months": 12,
      "deposits": 0,
      "interest": 500,
      "totalDeposits": 10000,
      "totalInterest": 500,
      "balance": 10500
    },
    {
      "year": 2,
      "months": 24,
      "deposits": 0,
      "interest": 525,
      "totalDeposits": 10000,
      "totalInterest": 1025,
      "balance": 11025
    },
    {
      "year": 3,
      "months": 36,
      "deposits": 0,
      "interest": 551.25,
      "totalDeposits": 10000,
      "totalInterest": 1576.25,
      "balance": 11576.25
    },
    {
      "year": 4,
      "months": 48,
      "deposits": 0,
      "interest": 578.81,
      "totalDeposits": 10000,
      "totalInterest": 2155.06,
      "balance": 12155.06
    },
    {
      "year": 5,
      "months": 60,
      "deposits": 0,
      "interest": 607.75,
      "totalDeposits": 10000,
      "totalInterest": 2762.82,
      "balance": 12762.82
    },
    {
      "year": 6,
      "months": 72,
      "deposits": 0,
      "interest": 638.14,
      "totalDeposits": 10000,
      "totalInterest": 3400.96,
      "balance": 13400.96
    },
    {
      "year": 7,
      "months": 84,
      "deposits": 0,
      "interest": 670.05,
      "totalDeposits": 10000,
      "totalInterest": 4071,
      "balance": 14071
    },
    {
      "year": 8,
      "months": 96,
      "deposits": 0,
      "interest": 703.55,
      "totalDeposits": 10000,
      "totalInterest": 4774.55,
      "balance": 14774.55
    },
    {
      "year": 9,
      "months": 108,
      "deposits": 0,
      "interest": 738.73,
      "totalDeposits": 10000,
      "totalInterest": 5513.28,
      "balance": 15513.28
    },
    {
      "year": 10,
      "months": 120,
      "deposits": 0,
      "interest": 775.66,
      "totalDeposits": 10000,
      "totalInterest": 6288.95,
      "balance": 16288.95
    }
  ],
  "summary": "Initial 10,000.00 + 0.00/month at 5% (compounded yearly) for 10 years\nFinal balance: 16,288.95\nYour deposits: 10,000.00\nInterest earned: 6,288.95"
}
calc_date_diffDate difference / add days

Two date jobs. difference: days between two dates (optionally counting the end day), as years/months/days, weeks, whole months, working days and weekend days for a chosen weekend (Fri–Sat default, Sat–Sun, Fri only, Sun only; public holidays are not counted). add: add or subtract days, business days, weeks, months or years to a date (month ends clamp: Jan 31 + 1 month = Feb 28/29). Results include the weekday and Umm al-Qura Hijri date. Dates are ISO YYYY-MM-DD; for Hijri ⇄ Gregorian conversion use calc_hijri. Web version: https://s3eidat.github.io/date-diff/

InputTypeDescription
mode"difference" | "add"
default "difference"
difference between two dates, or add/subtract an amount.
startstringdifference: start date YYYY-MM-DD (default today).
endstringdifference: end date YYYY-MM-DD (default today).
inclusiveboolean
default false
difference: count the end date too.
weekend"fri-sat" | "sat-sun" | "fri" | "sun"
default "fri-sat"
Weekend days skipped by working-day counts.
datestringadd: the starting date YYYY-MM-DD (default today).
amountintegeradd: how many units to add (use subtract to go back).
unit"days" | "business_days" | "weeks" | "months" | "years"
default "days"
add: unit of amount.
subtractboolean
default false
add: go back in time instead.

Example: 5 days, 5 working days

{
  "start": "2026-10-26",
  "end": "2026-10-31",
  "weekend": "sat-sun"
}

Result

{
  "mode": "difference",
  "days": 5,
  "years": 0,
  "months": 0,
  "remainderDays": 5,
  "text": "5 days",
  "weeks": 0,
  "weeksRemainderDays": 5,
  "wholeMonths": 0,
  "workingDays": 5,
  "weekendDays": 0,
  "endBeforeStart": false,
  "summary": "From Monday, 26 October 2026 to Saturday, 31 October 2026: 5 days (5 days), 5 working days with Sat–Sun weekends"
}

Example: 2026-11-06

{
  "mode": "add",
  "date": "2026-10-30",
  "amount": 5,
  "unit": "business_days",
  "weekend": "sat-sun"
}

Result

{
  "mode": "add",
  "result": {
    "date": "2026-11-06",
    "weekday": "Friday",
    "long": "Friday, 6 November 2026",
    "hijri": "Jumada I 26, 1448 AH",
    "hijriArabic": "٢٦ جمادى الأولى ١٤٤٨ هـ"
  },
  "calendarDays": 7,
  "note": "Skips Sat–Sun weekends; public holidays are not counted.",
  "summary": "Friday, 30 October 2026 + 5 business days = Friday, 6 November 2026 (Jumada I 26, 1448 AH)"
}
calc_discountDiscount / sale price calculator

Three shop maths jobs. sale: price after one or two stacked discounts (each a percent or a fixed amount) plus optional tax/VAT, with what you save and the effective total discount (stacked percents multiply). reverse: the discount % between an old and a new price. compare: which of two deals is cheaper per unit (price and amount of each). Money is rounded to the cent at each step like a till. Web version: https://s3eidat.github.io/discount-calc/

InputTypeDescription
mode"sale" | "reverse" | "compare"
default "sale"
Which calculator.
pricenumber | stringsale: original price.
discountnumber | stringsale: first discount (percent by default).
discountType"percent" | "amount"
default "percent"
sale: first discount is a percent or a fixed amount.
extraDiscountnumber | stringsale: optional second discount applied after the first.
extraDiscountType"percent" | "amount"
default "percent"
sale: second discount type.
taxPercentnumber | stringsale: optional tax / VAT percent added after the discounts.
wasnumber | stringreverse: the old price.
nownumber | stringreverse: the new price.
unitstringcompare: the unit name (kg, L, item…). Default "unit".
priceAnumber | stringcompare: price of deal A.
amountAnumber | stringcompare: amount of deal A (e.g. 0.5 for 500 g when unit is kg).
priceBnumber | stringcompare: price of deal B.
amountBnumber | stringcompare: amount of deal B.

Example: youPay 83.52 (28% off in total)

{
  "price": 100,
  "discount": 20,
  "extraDiscount": 10,
  "taxPercent": 16
}

Result

{
  "mode": "sale",
  "sale": {
    "originalPrice": 100,
    "discount1": 20,
    "discount2": 8,
    "priceAfterDiscounts": 72,
    "tax": 11.52,
    "youPay": 83.52,
    "youSave": 28,
    "effectiveDiscountPercent": 28,
    "wasWithTax": 116,
    "summary": "Original 100.00, Discount 20% −20.00, Extra 10% −8.00, tax +11.52\nYou pay 83.52 (save 28.00, 28%)"
  }
}

Example: B is 20% cheaper per kg

{
  "mode": "compare",
  "unit": "kg",
  "priceA": 4.5,
  "amountA": 0.5,
  "priceB": 7.2,
  "amountB": 1
}

Result

{
  "mode": "compare",
  "compare": {
    "unitPriceA": 9,
    "unitPriceB": 7.2,
    "cheaper": "B",
    "percentCheaper": 20,
    "text": "Deal B is 20% cheaper per kg (A: 9, B: 7.2 per kg)"
  }
}
calc_due_datePregnancy due date

Estimated pregnancy due date from the first day of the last period (Naegele's rule, adjusted for cycle length 20–45 days), the conception date, or an IVF embryo transfer date (day-3 or day-5 embryo). Also gives the estimated conception date, full-term date, how far along you are on a given day (weeks + days, trimester, days to go) and a dated timeline of common milestones (weeks 4–42), optionally with Hijri dates. Estimate only, not medical advice. Web version: https://s3eidat.github.io/due-date/

InputTypeDescription
method"lmp" | "conception" | "ivf"
default "lmp"
lmp = first day of last period; conception; ivf = embryo transfer date.
date requiredstringThe date for that method, YYYY-MM-DD.
cycleLengthinteger
default 28
lmp: average cycle length in days (default 28).
ivfEmbryoDay3 | 5
default 5
ivf: embryo age at transfer, day 3 or day 5.
todaystringWork out progress on this date, YYYY-MM-DD (default today).
hijriboolean
default false
Also give Umm al-Qura Hijri dates.

Example: due 2026-10-08, 8 weeks 3 days, 1st trimester

{
  "method": "lmp",
  "date": "2026-01-01",
  "today": "2026-03-01"
}

Result

{
  "dueDate": "2026-10-08",
  "dueDateLong": "Thursday, 8 October 2026",
  "conception": "2026-01-15",
  "fullTermFrom": "2026-09-17",
  "equivalentLastPeriod": "2026-01-01",
  "status": "Today you are 8 weeks and 3 days (8+3).",
  "progress": {
    "weeks": 8,
    "days": 3,
    "totalDays": 59,
    "trimester": 1,
    "daysToGo": 221,
    "percent": 21
  },
  "milestones": [
    {
      "week": 4,
      "date": "2026-01-29",
      "text": "A home pregnancy test can usually turn positive around now",
      "past": true
    },
    {
      "week": 6,
      "date": "2026-02-12",
      "text": "A heartbeat can often be seen on an early ultrasound (weeks 6–7)",
      "past": true
    },
    {
      "week": 8,
      "date": "2026-02-26",
      "text": "First prenatal visit is commonly booked between weeks 8 and 10",
      "past": true
    },
    {
      "week": 11,
      "date": "2026-03-19",
      "text": "First-trimester screening window (nuchal scan, weeks 11–13+6)",
      "past": false
    },
    {
      "week": 14,
      "date": "2026-04-09",
      "text": "Second trimester begins",
      "past": false
    },
    {
      "week": 16,
      "date": "2026-04-23",
      "text": "Some people start to feel first movements (weeks 16–22)",
      "past": false
    },
    {
      "week": 18,
      "date": "2026-05-07",
      "text": "Detailed anatomy scan window (weeks 18–22)",
      "past": false
    },
    {
      "week": 24,
      "date": "2026-06-18",
      "text": "Glucose screening for gestational diabetes is often done (weeks 24–28)",
      "past": false
    },
    {
      "week": 28,
      "date": "2026-07-16",
      "text": "Third trimester begins",
      "past": false
    },
    {
      "week": 32,
      "date": "2026-08-13",
      "text": "Growth checks and more frequent visits are common from here",
      "past": false
    },
    {
      "week": 37,
      "date": "2026-09-17",
      "text": "Early term",
      "past": false
    },
    {
      "week": 39,
      "date": "2026-10-01",
      "text": "Full term",
      "past": false
    },
    {
      "week": 40,
      "date": "2026-10-08",
      "text": "Estimated due date",
      "past": false
    },
    {
      "week": 41,
      "date": "2026-10-15",
      "text": "Late term",
      "past": false
    },
    {
      "week": 42,
      "date": "2026-10-22",
      "text": "Post-term: care teams usually plan delivery before now",
      "past": false
    }
  ],
  "method": "Due date = first day of last period + 280 days, adjusted by your cycle length minus 28.",
  "summary": "Estimated due date: Thursday, 8 October 2026\nToday: 8 weeks 3 days, trimester 1\nEstimated conception: Thursday, 15 January 2026\n(Due date = first day of last period + 280 days, adjusted by your cycle length minus 28. Estimate only, not medical advice.)"
}
calc_equationEquation solver

Solve equations exactly with worked steps, like the website: linear a·x + b = c, quadratic a·x² + b·x + c = 0 (exact rational or surd roots like 1/2 + √5/2, complex roots), cubic a·x³ + b·x² + c·x + d = 0 (rational root test, else trigonometric/Cardano to 10 significant digits), and systems of 2 or 3 linear equations (Gauss–Jordan; no solution and infinitely many solutions are detected and parametrised). Coefficients may be integers, decimals, fractions ("3/4"), mixed numbers ("-1 1/2") or e-notation. Web version: https://s3eidat.github.io/equation-solver/

InputTypeDescription
type required"linear" | "quadratic" | "cubic" | "system"Equation type.
coefficients(number | string)[]linear: [a, b, c] for a·x + b = c; quadratic: [a, b, c]; cubic: [a, b, c, d].
system((number | string)[])[]system: rows of the augmented matrix, 2 rows of [a, b, rhs] (x, y) or 3 rows of [a, b, c, rhs] (x, y, z).

Example: x = 1/2 ± √5/2

{
  "type": "quadratic",
  "coefficients": [
    1,
    -1,
    -1
  ]
}

Result

{
  "equation": "x² − x − 1 = 0",
  "kind": "two",
  "roots": [
    {
      "text": "1/2 − √5/2 ≈ −0.6180339887",
      "re": -0.618033988749895,
      "im": 0,
      "exact": true
    },
    {
      "text": "1/2 + √5/2 ≈ 1.618033989",
      "re": 1.61803398874989,
      "im": 0,
      "exact": true
    }
  ],
  "discriminant": "5",
  "result": [
    "x₁ = 1/2 − √5/2 ≈ −0.6180339887",
    "x₂ = 1/2 + √5/2 ≈ 1.618033989",
    "Discriminant Δ = 5"
  ],
  "steps": [
    {
      "text": "Equation: x² − x − 1 = 0, with a = 1, b = −1, c = −1"
    },
    {
      "text": "Discriminant: Δ = b² − 4ac = (−1)² − 4·(1)·(−1) = 5"
    },
    {
      "text": "√Δ = √5 = √5"
    },
    {
      "text": "Quadratic formula: x = (−b ± √Δ) / 2a = 1/2 ± √5/2"
    },
    {
      "text": "Δ > 0 and not a perfect square: two irrational real roots."
    }
  ]
}

Example: x = 5, y = 3, z = −2

{
  "type": "system",
  "system": [
    [
      1,
      1,
      1,
      6
    ],
    [
      0,
      2,
      5,
      -4
    ],
    [
      2,
      5,
      -1,
      27
    ]
  ]
}

Result

{
  "equation": "x + y + z = 6   ·   2y + 5z = −4   ·   2x + 5y − z = 27",
  "kind": "one",
  "solution": {
    "x": {
      "exact": "5",
      "value": 5
    },
    "y": {
      "exact": "3",
      "value": 3
    },
    "z": {
      "exact": "−2",
      "value": -2
    }
  },
  "result": [
    "x = 5",
    "y = 3",
    "z = −2"
  ],
  "steps": [
    {
      "text": "Augmented matrix",
      "matrix": [
        [
          "1",
          "1",
          "1",
          "6"
        ],
        [
          "0",
          "2",
          "5",
          "−4"
        ],
        [
          "2",
          "5",
          "−1",
          "27"
        ]
      ]
    },
    {
      "text": "Column x: R3 ← R3 − 2R1",
      "matrix": [
        [
          "1",
          "1",
          "1",
          "6"
        ],
        [
          "0",
          "2",
          "5",
          "−4"
        ],
        [
          "0",
          "3",
          "−3",
          "15"
        ]
      ]
    },
    {
      "text": "Column y: R2 ← R2 ÷ 2,  R1 ← R1 − R2,  R3 ← R3 − 3R2",
      "matrix": [
        [
          "1",
          "0",
          "−3/2",
          "8"
        ],
        [
          "0",
          "1",
          "5/2",
          "−2"
        ],
        [
          "0",
          "0",
          "−21/2",
          "21"
        ]
      ]
    },
    {
      "text": "Column z: R3 ← R3 ÷ (−21/2),  R1 ← R1 + (3/2)R3,  R2 ← R2 − (5/2)R3",
      "matrix": [
        [
          "1",
          "0",
          "0",
          "5"
        ],
        [
          "0",
          "1",
          "0",
          "3"
        ],
        [
          "0",
          "0",
          "1",
          "−2"
        ]
      ]
    },
    {
      "text": "Read off the solution: x = 5, y = 3, z = −2"
    },
    {
      "text": "Check: substituting back satisfies every equation ✓"
    }
  ]
}
calc_expressionScientific calculator

Evaluate a maths expression safely with the website's own parser (never eval): + − × ÷ ^ (right-associative), implicit multiplication (2(3+4), 2x), postfix ! and %, √, brackets; functions sin cos tan sec csc cot (+ asin acos atan atan2) in degrees (default), radians or grads, sinh cosh tanh and inverses, sqrt cbrt root(x,n) abs exp ln log(x[,base]) log10 log2 floor ceil round sign min max mod nCr nPr fact gamma; constants pi π e tau phi; "ans" and your own variables. Results to 12 significant digits, plain or scientific notation. Web version: https://s3eidat.github.io/sci-calc/

InputTypeDescription
expression requiredstringThe expression, e.g. "sin(30) + 2^3^2", "5!/(2!×3!)", "200×15%".
angle"deg" | "rad" | "grad"
default "deg"
Angle unit for trig functions.
variablesobjectVariables to use, e.g. {"x": 2, "r": 5}.
ansnumberValue of "ans" (the previous answer). Default 0.
format"norm" | "sci"
default "norm"
Display: normal, or always scientific notation.

Example

{
  "expression": "sin(30) + cos(60)"
}

Result

{
  "value": 1,
  "display": "1"
}

Example

{
  "expression": "nCr(52, 5)"
}

Result

{
  "value": 2598960,
  "display": "2,598,960"
}
calc_fractionFraction calculator

Exact fraction maths with big integers (never rounded) and worked steps. calculate: an expression with fractions, mixed numbers ("1 1/2"), decimals and repeating decimals ("0.(3)"), + − × ÷ ^ and brackets (a/b binds tighter than ÷). decimal_to_fraction: "0.75", "0.1(6)" (brackets mark the repeating part), with close small-denominator approximations. fraction_to_decimal: exact decimal with the repeating block, and percent. compare: put two or more fractions in order. Web version: https://s3eidat.github.io/fraction-calc/

InputTypeDescription
mode"calculate" | "decimal_to_fraction" | "fraction_to_decimal" | "compare"
default "calculate"
What to do.
input requiredstringcalculate: "1 1/2 + 3/4 × 2/5"; decimal_to_fraction: "0.1(6)"; fraction_to_decimal: "5/7"; compare: "2/3, 3/5, 7/10".

Example: 9/4 = 2 1/4 = 2.25

{
  "input": "1 1/2 + 3/4"
}

Result

{
  "mode": "calculate",
  "fraction": "9/4",
  "numerator": "9",
  "denominator": "4",
  "mixed": "2 1/4",
  "decimal": "2.25",
  "repeating": false,
  "percent": "225%",
  "steps": [
    {
      "text": "Mixed number to improper fraction",
      "math": "1 1/2 = 3/2 (1 × 2 + 1)"
    },
    {
      "text": "Lowest common denominator of 2 and 4 is 4",
      "math": "3/2 + 3/4 = 6/4 + 3/4"
    },
    {
      "text": "Add the numerators",
      "math": "= 9/4"
    }
  ]
}

Example: 1/6

{
  "mode": "decimal_to_fraction",
  "input": "0.1(6)"
}

Result

{
  "mode": "decimal_to_fraction",
  "fraction": "1/6",
  "numerator": "1",
  "denominator": "6",
  "decimal": "0.1(6)",
  "repeating": true,
  "rounded": "0.166667",
  "percent": "16.(6)%",
  "steps": [
    {
      "text": "Let x = 0.1(6). The block \"6\" repeats every 1 digit."
    },
    {
      "text": "Multiply by 100 and by 10 so the repeating tails line up",
      "math": "100x = 16.(6);  10x = 1.(6)"
    },
    {
      "text": "Subtract: the repeating parts cancel",
      "math": "90x = 15"
    },
    {
      "text": "Divide",
      "math": "x = 15/90"
    },
    {
      "text": "Simplify (divide by 15)",
      "math": "x = 1/6"
    }
  ]
}
calc_fuel_costFuel cost of a trip

Fuel needed and cost of a trip from distance (km or miles, optionally round trip), fuel price (per litre, US gallon or UK gallon) and consumption in L/100 km, km/L, mpg (US) or mpg (UK); split between people; cost per km/mile and per 100. Optionally compare a second car (its own consumption and, if different, fuel price). Also converts the consumption to the other units. Web version: https://s3eidat.github.io/fuel-cost/

InputTypeDescription
distance requirednumber | stringTrip distance (one way unless roundTrip).
distanceUnit"km" | "mi"
default "km"
Distance unit.
roundTripboolean
default false
Double the distance (there and back).
price requirednumber | stringFuel price per priceUnit.
priceUnit"L" | "galUS" | "galUK"
default "L"
The price is per litre, US gallon or UK gallon.
consumption requirednumber | stringCar consumption / economy, e.g. 7.5 (L/100 km) or 35 (mpg).
consumptionUnit"l100" | "kml" | "mpgus" | "mpguk"
default "l100"
l100 = L/100 km, kml = km/L, mpgus, mpguk.
peopleinteger
default 1
Split the cost between this many people.
currencystringCurrency label for the text, e.g. "JD" or "{{body}}quot;.
namestringName of the car (default "Car A").
compareobjectOptional second car to compare.

Example: 70.35 L, cost 63.32, 21.11 each

{
  "distance": 335,
  "roundTrip": true,
  "price": 0.9,
  "consumption": 10.5,
  "people": 3,
  "currency": "JD"
}

Result

{
  "distanceKm": 670,
  "litres": 70.35,
  "gallons": 18.58,
  "gallonUnit": "US gal",
  "cost": 63.32,
  "costEach": 21.11,
  "costPerDistanceUnit": 0.0945,
  "costPer100": 9.45,
  "pricePerLitre": 0.9,
  "l100km": 10.5,
  "consumptionIn": {
    "l100": 10.5,
    "kml": 9.52,
    "mpgus": 22.4,
    "mpguk": 26.9
  },
  "summary": "Trip: 670 km (round trip), Car A at 10.5 L/100 km: 70.4 L = JD 63.32 (JD 21.11 each for 3)"
}
calc_gpaGPA calculator

Credit-weighted semester GPA, cumulative GPA (with previous credits and GPA) and the average you need over the remaining credits to reach a target. Scales: 4.0 (A…F, no A+), 4.3 (A+ = 4.3), 5.0 (A+ 5, A 4.75, B+ 4.5 … F 1) with letter grades, or "percent" with numeric marks between a minimum and maximum (default 35–100). Courses with no credits or an invalid grade are skipped (and listed). Up to 40 courses. Web version: https://s3eidat.github.io/gpa-calc/

InputTypeDescription
scale"4.0" | "4.3" | "5.0" | "percent"
default "4.0"
Grading scale.
percentMinnumber | stringpercent scale: lowest passing mark (default 35).
percentMaxnumber | stringpercent scale: highest mark (default 100).
courses requiredobject[]This semester's courses.
previousCreditsnumber | stringCredits completed before this semester.
previousGpanumber | stringGPA before this semester.
targetGpanumber | stringTarget cumulative GPA.
remainingCreditsnumber | stringCredits still to take after this semester (for the target).

Example: semester 2.99, cumulative 3.15, need 3.71 (about A-)

{
  "courses": [
    {
      "credits": 3,
      "grade": "A"
    },
    {
      "credits": 3,
      "grade": "B+"
    },
    {
      "credits": 4,
      "grade": "C"
    }
  ],
  "previousCredits": 30,
  "previousGpa": 3.2,
  "targetGpa": 3.3,
  "remainingCredits": 15
}

Result

{
  "scale": "4.0",
  "semester": {
    "gpa": 2.99,
    "display": "2.99",
    "credits": 10,
    "points": 29.9
  },
  "cumulative": {
    "gpa": 3.15,
    "display": "3.15",
    "credits": 40
  },
  "skipped": [],
  "target": {
    "status": "needed",
    "neededAverage": 3.71,
    "letter": "A",
    "message": "You need an average of 3.71 over the next 15 credits (about A or better) to reach 3.30."
  },
  "summary": "Semester GPA: 2.99 (10 credits)\nCumulative GPA: 3.15 (40 credits)\nYou need an average of 3.71 over the next 15 credits (about A or better) to reach 3.30."
}
calc_hijriHijri ⇄ Gregorian converter

Convert between Gregorian and Hijri dates with the Umm al-Qura calendar (the same Intl calendar the website uses), both ways. Modes: to_hijri (a Gregorian date), to_gregorian (Hijri year, month 1–12, day), month (length of a Hijri month and its Gregorian start/end), age (age in Hijri and Gregorian years, next Hijri birthday). Results come in English and Arabic (Arabic-Indic digits by default; Gregorian month names in the Egyptian/Gulf or Levant style). Supported range 1300–1600 AH (1882–2174). Web version: https://s3eidat.github.io/hijri-converter/

InputTypeDescription
mode required"to_hijri" | "to_gregorian" | "month" | "age"Which conversion.
datestringto_hijri: Gregorian date YYYY-MM-DD (default today).
hijriYearintegerto_gregorian / month: Hijri year (1300–1600).
hijriMonthintegerto_gregorian / month: Hijri month 1–12 (1 = Muharram, 9 = Ramadan).
hijriDayintegerto_gregorian: Hijri day 1–30.
birthDatestringage: Gregorian date of birth YYYY-MM-DD.
onDatestringage: age on this Gregorian date (default today).
arabicDigitsboolean
default true
Use Arabic-Indic digits in the Arabic text.
gregorianArabicMonths"intl" | "levant"
default "intl"
Arabic Gregorian month names: intl (Egypt/Gulf style, January = Yanayir) or levant (Syria/Jordan style, January = Kanun al-Thani).

Example: 24 Ramadan 1420 AH (Saturday)

{
  "mode": "to_hijri",
  "date": "2000-01-01"
}

Result

{
  "mode": "to_hijri",
  "day": {
    "gregorian": {
      "date": "2000-01-01",
      "weekday": "Saturday",
      "text": "Saturday, 1 January 2000",
      "arabic": "السبت ١ يناير ٢٠٠٠ م"
    },
    "hijri": {
      "year": 1420,
      "month": 9,
      "day": 24,
      "monthName": "Ramadan",
      "monthNameArabic": "رمضان",
      "iso": "1420-09-24",
      "text": "Saturday, 24 Ramadan 1420 AH",
      "arabic": "السبت ٢٤ رمضان ١٤٢٠ هـ"
    },
    "approximate": false
  },
  "summary": "Saturday, 1 January 2000 = 24 Ramadan 1420 AH · السبت ٢٤ رمضان ١٤٢٠ هـ"
}

Example: 2026-02-18 (Wednesday)

{
  "mode": "to_gregorian",
  "hijriYear": 1447,
  "hijriMonth": 9,
  "hijriDay": 1
}

Result

{
  "mode": "to_gregorian",
  "day": {
    "gregorian": {
      "date": "2026-02-18",
      "weekday": "Wednesday",
      "text": "Wednesday, 18 February 2026",
      "arabic": "الأربعاء ١٨ فبراير ٢٠٢٦ م"
    },
    "hijri": {
      "year": 1447,
      "month": 9,
      "day": 1,
      "monthName": "Ramadan",
      "monthNameArabic": "رمضان",
      "iso": "1447-09-01",
      "text": "Wednesday, 1 Ramadan 1447 AH",
      "arabic": "الأربعاء ١ رمضان ١٤٤٧ هـ"
    },
    "approximate": false
  },
  "summary": "Wednesday, 18 February 2026 = 1 Ramadan 1447 AH · الأربعاء ١ رمضان ١٤٤٧ هـ"
}
calc_hoursWork hours / time card

Weekly time card: add shifts (start and end "HH:MM", unpaid break minutes; an end at or before the start is an overnight shift), up to 6 shifts a day. Totals per shift, per day and per week in h:mm and decimal hours; optional rounding of each shift to 5, 6 or 15 minutes; overtime over N hours a day and/or N regular hours a week (daily first, then weekly), paid at a multiplier (default 1.5); pay at an hourly rate. Web version: https://s3eidat.github.io/hours-calc/

InputTypeDescription
shifts requiredobject[]The week's shifts.
dailyOvertimeHoursnumberHours per day after which time is overtime (e.g. 8).
weeklyOvertimeHoursnumberRegular hours per week after which time is overtime (e.g. 40).
overtimeMultipliernumber
default 1.5
Overtime pay multiplier.
hourlyRatenumberPay per regular hour.
currencystringCurrency label for the summary.
roundMinutes0 | 5 | 6 | 15
default 0
Round each shift to the nearest 5, 6 or 15 minutes (0 = off).
weekStart"saturday" | "sunday" | "monday"
default "saturday"
First day of the week (order of the days).
weekOfstringOptional: any date in that week (YYYY-MM-DD) to date each day.

Example: 15:30 total, pay 310

{
  "shifts": [
    {
      "day": "monday",
      "start": "09:00",
      "end": "17:30",
      "breakMinutes": 30
    },
    {
      "day": "tuesday",
      "start": "22:00",
      "end": "06:00",
      "breakMinutes": 30
    }
  ],
  "hourlyRate": 20
}

Result

{
  "shifts": [
    {
      "day": "Monday",
      "start": "09:00",
      "end": "17:30",
      "breakMinutes": 30,
      "minutes": 480,
      "hm": "8:00",
      "hours": "8.00",
      "overnight": false
    },
    {
      "day": "Tuesday",
      "start": "22:00",
      "end": "06:00",
      "breakMinutes": 30,
      "minutes": 450,
      "hm": "7:30",
      "hours": "7.50",
      "overnight": true
    }
  ],
  "days": [
    {
      "day": "Saturday",
      "minutes": 0,
      "hm": "0:00",
      "hours": "0.00",
      "overtimeMinutes": 0
    },
    {
      "day": "Sunday",
      "minutes": 0,
      "hm": "0:00",
      "hours": "0.00",
      "overtimeMinutes": 0
    },
    {
      "day": "Monday",
      "minutes": 480,
      "hm": "8:00",
      "hours": "8.00",
      "overtimeMinutes": 0
    },
    {
      "day": "Tuesday",
      "minutes": 450,
      "hm": "7:30",
      "hours": "7.50",
      "overtimeMinutes": 0
    },
    {
      "day": "Wednesday",
      "minutes": 0,
      "hm": "0:00",
      "hours": "0.00",
      "overtimeMinutes": 0
    },
    {
      "day": "Thursday",
      "minutes": 0,
      "hm": "0:00",
      "hours": "0.00",
      "overtimeMinutes": 0
    },
    {
      "day": "Friday",
      "minutes": 0,
      "hm": "0:00",
      "hours": "0.00",
      "overtimeMinutes": 0
    }
  ],
  "total": {
    "minutes": 930,
    "hm": "15:30",
    "hours": "15.50"
  },
  "regular": {
    "minutes": 930,
    "hm": "15:30",
    "hours": "15.50"
  },
  "overtime": {
    "minutes": 0,
    "hm": "0:00",
    "hours": "0.00",
    "daily": 0,
    "weekly": 0
  },
  "pay": {
    "regular": 310,
    "overtime": 0,
    "total": 310
  },
  "note": "No overtime rules set; give dailyOvertimeHours or weeklyOvertimeHours to split regular and overtime hours. Rate: 20.00 per hour.",
  "summary": "Monday: 09:00–17:30 (30 min break) = 8:00 (8.00 h)\nTuesday: 22:00–06:00 (30 min break) = 7:30 (7.50 h)\nTotal: 15:30 (15.50 h) · regular 15:30 · overtime 0:00\nPay: 310.00 at 20.00/h, overtime ×1.5"
}
calc_loanLoan / mortgage calculator

Monthly payment, total interest, total paid and payoff month for a fixed-rate loan or mortgage, with an optional extra monthly payment (shows interest saved and how much sooner it ends) and an amortization schedule by year or by month (or none). Maths is in whole cents like the website; the last payment absorbs the rounding so the balance ends at 0. Rate 0–100 %, term 1 month to 100 years. Web version: https://s3eidat.github.io/loan-calc/

InputTypeDescription
amount requirednumber | stringLoan amount (principal), e.g. 250000 or "250,000".
annualRate requirednumber | stringAnnual interest rate in percent, e.g. 6.5. 0 is allowed.
term requirednumber | stringLoan term, in years (default) or months (see termUnit).
termUnit"years" | "months"
default "years"
Unit of "term".
extraMonthlynumber | stringOptional extra principal paid every month.
startMonthstringFirst payment month "YYYY-MM". Default: next month.
schedule"yearly" | "monthly" | "none"
default "yearly"
Amortization table: per calendar year, per month, or omit it.

Example: monthlyPayment 1199.10, totalInterest 231,677.04, 360 months

{
  "amount": 200000,
  "annualRate": 6,
  "term": 30,
  "schedule": "none"
}

Result

{
  "monthlyPayment": 1199.1,
  "extraMonthly": 0,
  "months": 360,
  "duration": "30 years",
  "totalInterest": 231677.04,
  "totalPaid": 431677.04,
  "firstPayment": "2026-02",
  "payoffMonth": "2056-01",
  "payoffLabel": "Jan 2056",
  "summary": "Loan 200,000.00 at 6% for 30 years\nMonthly payment: 1,199.10\nTotal interest: 231,677.04\nTotal paid: 431,677.04\nPaid off: Jan 2056 (30 years)"
}
calc_percentagePercentage calculator

Every percentage question the website answers: "X% of Y" (percent_of: percent, value), "X is what % of Y" (what_percent: part, whole), percent change from A to B (change: from, to), increase/decrease a value by X% (increase/decrease: value, percent), find the original before a X% increase/decrease (reverse_increase/reverse_decrease: value, percent), and profit margin vs markup (margin_markup: cost, price). Returns the number, a display string and the worked formula. Web version: https://s3eidat.github.io/percentage-calc/

InputTypeDescription
mode required"percent_of" | "what_percent" | "change" | "increase" | "decrease" | "reverse_increase" | "reverse_decrease" | "margin_markup"Which question to answer (see the description for the fields each one uses).
percentnumber | stringThe percentage X (percent_of, increase, decrease, reverse_*).
valuenumber | stringThe value Y (percent_of: Y in "X% of Y"; increase/decrease: the starting value; reverse_*: the final value).
partnumber | stringwhat_percent: the part X in "X is what % of Y".
wholenumber | stringwhat_percent: the whole Y.
fromnumber | stringchange: the old value.
tonumber | stringchange: the new value.
costnumber | stringmargin_markup: cost price.
pricenumber | stringmargin_markup: selling price.

Example

{
  "mode": "percent_of",
  "percent": 15,
  "value": 200
}

Result

{
  "result": 30
}

Example: original 100

{
  "mode": "reverse_increase",
  "value": 115,
  "percent": 15
}

Result

{
  "mode": "reverse_increase",
  "result": 100,
  "formatted": "100",
  "explanation": "original = 115 ÷ (1 + 15/100) = 100 · the 15% part is 15"
}
calc_primesPrime number tools

Number theory with big integers, like the website. check: is a number prime (trial division, deterministic Miller-Rabin below 2^64, probabilistic above), its prime factorisation (Pollard rho), number of divisors, sum of divisors σ, Euler φ, the divisor list, previous/next prime and whether it is perfect; the number may be an expression like "2^61-1", "10!+1" or "1000003*1000033" (up to 400 digits). gcd_lcm: GCD and LCM of 2–50 numbers with Euclid's steps. range: list the primes between two numbers (below 10^15, at most 10,000,000 numbers per range). Web version: https://s3eidat.github.io/prime-tools/

InputTypeDescription
mode"check" | "gcd_lcm" | "range"
default "check"
What to do.
numberstring | numbercheck: the number or expression, e.g. "600851475143" or "2^89-1".
numbers(string | number)[]gcd_lcm: the whole numbers (2–50, expressions allowed).
fromintegerrange: start (inclusive).
tointegerrange: end (inclusive), below 10^15.
limitinteger
default 10000
range: return at most this many primes in the list (the count is always exact).

Example: composite = 71 × 839 × 1471 × 6857

{
  "number": "600851475143"
}

Result

{
  "mode": "check",
  "number": "600851475143",
  "digits": 12,
  "isPrime": false,
  "verdict": "Composite",
  "method": "Checked by trial division.",
  "factorization": "71 × 839 × 1471 × 6857",
  "factors": [
    {
      "prime": "71",
      "exponent": 1
    },
    {
      "prime": "839",
      "exponent": 1
    },
    {
      "prime": "1471",
      "exponent": 1
    },
    {
      "prime": "6857",
      "exponent": 1
    }
  ],
  "probablePrimeFactors": false,
  "divisorCount": "16",
  "divisorSum": "610544148480",
  "eulerPhi": "591194251200",
  "perfect": false,
  "divisors": [
    "1",
    "71",
    "839",
    "1471",
    "6857",
    "59569",
    "104441",
    "486847",
    "1234169",
    "5753023",
    "10086647",
    "87625999",
    "408464633",
    "716151937",
    "8462696833",
    "600851475143"
  ],
  "previousPrime": "600851475067",
  "nextPrime": "600851475149"
}

Example: gcd 42, lcm 1260

{
  "mode": "gcd_lcm",
  "numbers": [
    84,
    126,
    210
  ]
}

Result

{
  "mode": "gcd_lcm",
  "gcd": "42",
  "lcm": "1260",
  "steps": [
    {
      "head": "gcd(84, 126)",
      "lines": [
        "126 = 1 × 84 + 42",
        "84 = 2 × 42 + 0",
        "→ 42"
      ]
    },
    {
      "head": "lcm(84, 126) = 84 × 126 ÷ 42 = 252",
      "lines": []
    },
    {
      "head": "gcd(42, 210)",
      "lines": [
        "210 = 5 × 42 + 0",
        "→ 42"
      ]
    },
    {
      "head": "lcm(252, 210) = 252 × 210 ÷ 42 = 1260",
      "lines": []
    }
  ]
}

Example: 25 primes

{
  "mode": "range",
  "from": 1,
  "to": 100
}

Result

{
  "mode": "range",
  "count": 25,
  "primes": [
    2,
    3,
    5,
    7,
    11,
    13,
    17,
    19,
    23,
    29,
    31,
    37,
    41,
    43,
    47,
    53,
    59,
    61,
    67,
    71,
    73,
    79,
    83,
    89,
    97
  ],
  "truncated": false
}
calc_romanRoman numerals

Convert numbers to Roman numerals (1–3,999 in standard form; up to 3,999,999 with a vinculum bar, which multiplies by 1,000) and Roman numerals back to numbers with strict validation that explains mistakes (IIII, IC, VV, non-standard forms like IIX…) and the symbol-by-symbol working. Also writes a date in Roman numerals (day/month/year order and separator of your choice, years 1–3999). Web version: https://s3eidat.github.io/roman-numerals/

InputTypeDescription
mode required"to_roman" | "from_roman" | "date"to_roman (number → Roman), from_roman (Roman → number) or date.
numbernumber | stringto_roman: a whole number 1–3,999,999.
romanstringfrom_roman: the numeral, e.g. "MCMXCIV" (any case).
datestringdate: YYYY-MM-DD.
order"dmy" | "mdy" | "ymd"
default "dmy"
date: order of day, month, year.
separatorstring
default " · "
date: separator, e.g. " · ", ".", " - ", " / ".

Example

{
  "mode": "to_roman",
  "number": 1994
}

Result

{
  "roman": "MCMXCIV"
}

Example: invalid: write 99 as XCIX

{
  "mode": "from_roman",
  "roman": "IC"
}

Result

{
  "mode": "from_roman",
  "valid": false,
  "error": "\"IC\" is not a valid subtraction. Only IV, IX, XL, XC, CD and CM are allowed (99 is XCIX)."
}
calc_salarySalary / pay period converter

Convert pay between per hour, day, week, every 2 weeks, twice a month, month and year using your hours per week, days per week and weeks per year (defaults 40 / 5 / 52). Optional overtime (hours per week × multiplier, e.g. 1.25, 1.5, 2) and a raise given as a percent or as the new monthly pay. Amounts are rounded to 2 decimals. Web version: https://s3eidat.github.io/salary-calc/

InputTypeDescription
amount requirednumber | stringThe pay amount, e.g. 1200.
period"hour" | "day" | "week" | "biweek" | "semimonth" | "month" | "year"
default "month"
The period "amount" is paid for.
hoursPerWeeknumber | stringWorking hours per week (0–168, default 40).
daysPerWeeknumber | stringWorking days per week (0–7, default 5).
weeksPerYearnumber | stringPaid weeks per year (0–53, default 52).
overtimeHoursnumber | stringOptional overtime hours per week.
overtimeMultipliernumber | stringOvertime rate multiplier (default 1.5).
raisePercentnumber | stringOptional raise in percent (gives the new pay).
newMonthlyPaynumber | stringOr: the new monthly pay (gives the raise percent).
currencystringOptional ISO currency code for the text summary (e.g. USD, JOD).

Example: hour 25, day 200, month 4333.33, biweek 2000

{
  "amount": 52000,
  "period": "year"
}

Result

{
  "periods": {
    "hour": 25,
    "day": 200,
    "week": 1000,
    "biweek": 2000,
    "semimonth": 2166.67,
    "month": 4333.33,
    "year": 52000
  },
  "summary": "Per hour: 25.00\nPer day: 200.00\nPer week: 1,000.00\nEvery 2 weeks: 2,000.00\nTwice a month: 2,166.67\nPer month: 4,333.33\nPer year: 52,000.00\n(40 h/week, 5 days/week, 52 weeks/year)"
}
calc_sleepSleep cycle calculator

Sleep in whole cycles so you wake in light sleep: given a wake-up time, the bedtimes for 6, 5, 4 or 3 cycles; given a bedtime (or "now"), the wake-up times. Defaults: 90-minute cycles (70–110) and 15 minutes to fall asleep (0–60). With mode "bed"/"now" it also lists nap alarms (20, 30, 60 min and one full cycle). 12- or 24-hour clock. Web version: https://s3eidat.github.io/sleep-calc/

InputTypeDescription
mode"wake" | "bed" | "now"
default "wake"
wake: I must wake at "time"; bed: I go to bed at "time"; now: I go to bed now (server clock).
timestring"HH:MM" 24 h: the wake-up time (wake) or bedtime (bed). Default 07:00 / 23:00.
fallAsleepMinutesinteger
default 15
Minutes it takes you to fall asleep.
cycleMinutesinteger
default 90
Length of one sleep cycle.
clock"12" | "24"
default "12"
12-hour (AM/PM) or 24-hour times in the output.

Example: bed at 21:45 (6 cycles), 23:15 (5), 00:45 (4), 02:15 (3)

{
  "mode": "wake",
  "time": "07:00",
  "clock": "24"
}
calc_split_billSplit a bill

Split a restaurant bill with tax/service and tip, in whole cents so the shares always add up exactly (left-over cents go to the largest remainders). simple: one bill split evenly between N people (1–100). items: list who had what; each item is split between the people who shared it (nobody listed = everyone), and tax and tip are shared in proportion. Tip can be on the subtotal or on subtotal + tax; each share can be rounded up to a step (e.g. 0.5 or 1). Returns a ready-to-send summary. Web version: https://s3eidat.github.io/split-bill/

InputTypeDescription
mode"simple" | "items"
default "simple"
Even split, or itemised.
billnumber | stringsimple: the bill subtotal (before tax and tip).
peopleinteger | string[]simple: how many people (default 2). items: the names, e.g. ["Sara", "Omar", "Lina"].
itemsobject[]items: what was ordered.
taxPercentnumber
default 0
Tax / service charge percent.
tipPercentnumber
default 10
Tip percent (default 10).
tipOnTaxboolean
default false
Calculate the tip on subtotal + tax.
roundUpTonumber
default 0
Round each share up to a multiple of this (e.g. 0.5, 1, 5). 0 = exact.
currencystringCurrency, e.g. "JOD" (written after) or "{{body}}quot; (written before).

Example: 110.00 total: 36.67, 36.67, 36.66

{
  "bill": 100,
  "people": 3,
  "tipPercent": 10
}

Result

{
  "subtotal": 100,
  "tax": 0,
  "tip": 10,
  "total": 110,
  "collected": 110,
  "roundingExtra": 0,
  "shares": [
    {
      "name": "Person 1",
      "pays": 36.67
    },
    {
      "name": "Person 2",
      "pays": 36.67
    },
    {
      "name": "Person 3",
      "pays": 36.66
    }
  ],
  "summary": "🧾 *Bill split* — 3 people\nSubtotal: 100.00\nTip 10%: 10.00\nTotal: *110.00*\n\n2 × *36.67* + 1 × *36.66*"
}
calc_statsDescriptive statistics

Descriptive statistics for a list of numbers: count, sum, mean, median, mode(s), min, max, range, sample and population variance and SD, standard error, coefficient of variation, quartiles (inclusive = Excel QUARTILE.INC, exclusive = QUARTILE.EXC, or Tukey median-of-halves), IQR, 1.5×IQR outlier fences and outliers, a frequency table (values, or Sturges bins when there are more than 20 distinct values) and z-scores. Paste numbers separated by spaces, commas, semicolons, | or new lines; headers and text are skipped and reported. Commas can be separators, thousands marks or decimal commas. Up to 1,000,000 numbers. Web version: https://s3eidat.github.io/stats-calc/

InputTypeDescription
data requiredstring | number[]The numbers: a text blob (CSV column, list…) or an array of numbers.
commas"sep" | "thou" | "dec"
default "sep"
In text: commas separate values (sep), are thousands marks (thou: 1,234), or decimal commas (dec: 1,5).
quartileMethod"inc" | "exc" | "tukey"
default "inc"
Quartile method.
frequencyboolean
default true
Include the frequency table.
zScoresboolean
default false
Include a z-score for each value (first 1,000).

Example: mean 5, median 4.5, mode 4, population SD 2, sample SD 2.138

{
  "data": "2, 4, 4, 4, 5, 5, 7, 9"
}

Result

{
  "count": 8,
  "sum": 40,
  "mean": 5,
  "median": 4.5,
  "modes": [
    4
  ],
  "modeCount": 3,
  "min": 2,
  "max": 9,
  "range": 7,
  "sampleVariance": 4.57142857143,
  "sampleSD": 2.1380899353,
  "populationVariance": 4,
  "populationSD": 2,
  "standardError": 0.755928946018,
  "coefficientOfVariation": 42.761798706,
  "q1": 4,
  "q3": 5.5,
  "iqr": 1.5,
  "quartileMethod": "inclusive (Excel QUARTILE.INC, R type 7)",
  "lowerFence": 1.75,
  "upperFence": 7.75,
  "outliers": [
    9
  ],
  "whiskerLow": 2,
  "whiskerHigh": 7,
  "skipped": [],
  "skippedCount": 0,
  "frequency": [
    {
      "value": "2",
      "count": 1,
      "relative": 0.125,
      "cumulative": 1
    },
    {
      "value": "4",
      "count": 3,
      "relative": 0.375,
      "cumulative": 4
    },
    {
      "value": "5",
      "count": 2,
      "relative": 0.25,
      "cumulative": 6
    },
    {
      "value": "7",
      "count": 1,
      "relative": 0.125,
      "cumulative": 7
    },
    {
      "value": "9",
      "count": 1,
      "relative": 0.125,
      "cumulative": 8
    }
  ],
  "summary": "Count (n): 8\nSum: 40\nMean: 5\nMedian: 4.5\nMode: 4 (×3)\nMinimum: 2\nMaximum: 9\nRange: 7\nSample variance (s²): 4.571429\nSample SD (s): 2.13809\nPopulation variance (σ²): 4\nPopulation SD (σ): 2\nStandard error of the mean: 0.755929\nCoefficient of variation: 42.761799%\nQ1: 4\nQ3: 5.5\nIQR: 1.5\nQuartile method: inclusive (Excel QUARTILE.INC, R type 7)\nOutlier fences (1.5 × IQR): 1.75 and 7.75\nOutliers: 9"
}
calc_unit_convertUnit converter

Convert between units in 14 categories, the same table as the website: length, mass, temperature, volume, area (incl. dunam), speed, time, data (kB/MB vs KiB/MiB, bits), pressure, energy, power, angle, fuel economy (L/100km, km/L, mpg US/UK) and cooking (tsp, tbsp, cups, metric spoons). Give value + from + to (symbols like "km", "°F", "GiB" or names like "miles"; ASCII forms like "m2", "um" work), or a query like "5 km to mi". Set all=true for the value in every unit of the category, or list=true to see every category and unit. Web version: https://s3eidat.github.io/unit-convert/

InputTypeDescription
querystringA whole question, e.g. "5 km to mi" or "72 f in c".
valuenumber | stringThe number to convert.
fromstringUnit to convert from (symbol or name).
tostringUnit to convert to (symbol or name). Optional when all=true.
category"length" | "mass" | "temp" | "volume" | "area" | "speed" | "time" | "data" | "pressure" | "energy" | "power" | "angle" | "fuel" | "cooking"Category, to pick the right unit when a symbol exists in two (e.g. "pt" in volume and cooking).
allboolean
default false
Also return the value in every unit of the category.
listboolean
default false
Just list every category and its units.

Example: 3.106855961 mi

{
  "query": "5 km to mi"
}

Result

{
  "category": "length",
  "from": {
    "symbol": "km",
    "name": "kilometre"
  },
  "value": 5,
  "to": {
    "symbol": "mi",
    "name": "mile"
  },
  "result": 3.10685596119,
  "formatted": "3.106855961 mi",
  "formula": "5 km = 3.106855961 mi",
  "oneUnit": "1 km = 0.6213711922 mi"
}

Example

{
  "value": 100,
  "from": "°C",
  "to": "°F"
}

Result

{
  "result": 212
}
calc_zakatZakat calculator

Zakat on wealth: 2.5 % of net zakatable wealth (cash, bank, investments, trade goods, money owed to you, gold and silver, minus debts due now) when it reaches the nisab: the value of 85 g of pure gold (default) or 595 g of silver. Gold weight is converted to pure gold by karat (24/22/21/18). You give today's gold/silver price per gram in your currency: nothing is fetched from the network. An estimate only; consult a scholar. Web version: https://s3eidat.github.io/zakat-calc/

InputTypeDescription
cashnumber | stringCash in hand.
banknumber | stringBank balances.
investmentsnumber | stringShares, funds and other investments.
tradeGoodsnumber | stringValue of goods held for sale.
receivablesnumber | stringMoney owed to you that you expect back.
debtsnumber | stringDebts you must pay now (deducted).
goldGramsnumber | stringGold you own, in grams.
goldKarat24 | 22 | 21 | 18
default 21
Karat of that gold (default 21).
goldPricePerGramnumber | stringPrice of 1 g of pure (24k) gold. Needed for the gold nisab and to value gold.
silverGramsnumber | stringSilver you own, in grams.
silverPricePerGramnumber | stringPrice of 1 g of silver. Needed for the silver nisab and to value silver.
nisabBasis"gold" | "silver"
default "gold"
Which metal sets the nisab.
currencystringCurrency label for the summary (e.g. JOD).

Example: net 18250, nisab 5100, zakat 456.25

{
  "cash": 5000,
  "bank": 10000,
  "goldGrams": 100,
  "goldKarat": 21,
  "goldPricePerGram": 60,
  "debts": 2000
}

Result

{
  "pureGoldGrams": 87.5,
  "goldValue": 5250,
  "silverValue": 0,
  "totalAssets": 20250,
  "netWealth": 18250,
  "nisabBasis": "gold",
  "nisab": 5100,
  "due": true,
  "zakat": 456.25,
  "message": "456.25 is due: 2.5% of 18,250.00, above the nisab of 5,100.00.",
  "summary": "Zakat calculation\nNet zakatable wealth: 18,250.00\nNisab (gold): 5,100.00\nZakat due (2.5%): 456.25\n(Estimate only; consult a scholar.)"
}

Everyday & security

Prayer times, qibla, world clock, random picks, Secret Santa, passwords, encryption.

crypto_file_decryptDecrypt a file (.s3enc)

Decrypt an .s3enc file made by the S3eidat File Encrypt website or crypto_file_encrypt, verify every chunk, and write the original file back under its stored (sanitised) name. A wrong password, damage, truncation or reordering is reported and nothing is written. Web version: https://s3eidat.github.io/file-encrypt/

InputTypeDescription
file requiredstringThe .s3enc file: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
password requiredstringThe password. Normalised to Unicode NFC first, like the website.
outputstringWhere to write the decrypted file: a file path, or a folder ending in "/" (keeps the stored name). Default: the server output folder.
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example: Writes report.pdf

{
  "file": "/path/to/report.pdf.s3enc",
  "password": "correct horse battery staple"
}
crypto_file_encryptEncrypt a file (.s3enc)

Encrypt any file with a password into the website's .s3enc format: chunked AES-256-GCM, key from PBKDF2-SHA256 (600,000 rounds, random salt), original name/type/size stored encrypted inside, tamper/truncation/reorder-proof. Files made here open at https://s3eidat.github.io/file-encrypt/ and vice versa (crypto_file_decrypt). Whole file is processed in memory (size limit = server --max-mb). Web version: https://s3eidat.github.io/file-encrypt/

InputTypeDescription
file requiredstringThe file to encrypt: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
password requiredstringThe password. Normalised to Unicode NFC first, like the website.
namestringFile name to store inside (restored on decrypt). Default: the input file name.
mimeTypestringMIME type to store. Default: guessed from the extension like a browser would ("" if unknown).
chunkSizeinteger
default 4194304
Chunk size in bytes (website: 4 MiB).
iterationsinteger
default 600000
PBKDF2 rounds (website: 600,000).
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example: Writes report.pdf.s3enc

{
  "file": "/path/to/report.pdf",
  "password": "correct horse battery staple"
}
crypto_stego_hideHide a message in an image

Hide a text message inside an image (LSB steganography in R, G, B; the picture looks the same) and save it as a lossless PNG, optionally encrypted with a password (AES-256-GCM, PBKDF2-SHA256 600,000). Same bit layout as the website, so the PNG reveals at https://s3eidat.github.io/stego-image/ or with crypto_stego_reveal. Capacity ≈ width×height×3/8 − 10 bytes (−44 more when encrypted). Transparency is filled with white. The result is read back and checked before returning. Sending it through apps that recompress images (JPEG, chat apps) destroys the message. Web version: https://s3eidat.github.io/stego-image/

InputTypeDescription
image requiredstringThe cover image (PNG, JPEG, WebP…): a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
message requiredstringThe text to hide (UTF-8).
passwordstringOptional password to encrypt the message.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example: Writes photo-hidden.png

{
  "image": "/path/to/photo.jpg",
  "message": "Meet at the old library at 7.",
  "password": "correct horse battery staple"
}
crypto_stego_revealReveal a hidden message

Read a message hidden in a PNG by the S3eidat Stego Image website or crypto_stego_hide (decrypting it if a password was used). Reports when there is no message, e.g. because the image was recompressed (JPEG), resized or edited. Web version: https://s3eidat.github.io/stego-image/

InputTypeDescription
image requiredstringThe image with a hidden message (PNG): a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
passwordstringPassword, if the message is encrypted.

Example

{
  "image": "/path/to/photo-hidden.png",
  "password": "correct horse battery staple"
}
crypto_text_decryptDecrypt text (AES-256-GCM)

Decrypt an "S3E1.…" message made by the S3eidat Text Encrypt website or crypto_text_encrypt, with its password. Verifies integrity: a wrong password or any change to the text is reported, never garbage output. Accepts base64url and line breaks. Web version: https://s3eidat.github.io/text-encrypt/

InputTypeDescription
encryptedstringThe encrypted text, starting with S3E1.
pathstringOr: a file containing it (e.g. encrypted.txt).
password requiredstringThe password. Normalised to Unicode NFC first, like the website.
outputstringOptional: also save the decrypted message to this file.

Example

{
  "encrypted": "S3E1.AQEACSfAtcIyfKiQlnccWg4e2uFxBglGUizyF0IsTYNU0kSqKpIDX4xsscpbHaTSv0AGmLwAcP3l341prQNNoeukuzER22r7",
  "password": "s3eidat"
}

Result

{
  "text": "Hello, مرحبا 👋",
  "iterations": 600000
}
crypto_text_encryptEncrypt text (AES-256-GCM)

Encrypt a message with a password: AES-256-GCM, key from PBKDF2-SHA256 (600,000 rounds, random 16-byte salt, 12-byte IV), output as one "S3E1.…" base64 line. Exactly the website's format, so the result can be decrypted at https://s3eidat.github.io/text-encrypt/ or with crypto_text_decrypt. Each run gives a different output. Web version: https://s3eidat.github.io/text-encrypt/

InputTypeDescription
textstringThe message to encrypt.
pathstringOr: a UTF-8 text file to encrypt.
password requiredstringThe password. Normalised to Unicode NFC first, like the website.
iterationsinteger
default 600000
PBKDF2 rounds (website: 600,000).
outputstringOptional: also save the encrypted line to this file (website "Save" gives encrypted.txt).

Example: → "S3E1.AQEACSfA…"

{
  "text": "Meet at the old library, 7 pm.",
  "password": "correct horse battery staple"
}
password_checkPassword strength check

Estimate how strong a password really is, the way attackers guess (zxcvbn-style): finds common passwords (1,282-entry list), dictionary words (also reversed, capitalised or l33t), keyboard walks, sequences, repeats, years and dates, then the cheapest way to guess the whole thing. Returns a 0–4 score, entropy bits, guesses, crack time for 4 attack scenarios, the pieces found, and advice. Offline only: no breach lookups, nothing stored or logged. Web version: https://s3eidat.github.io/password-check/

InputTypeDescription
password requiredstringThe password to check (only the first 256 characters are analysed in detail).
showPiecesboolean
default true
Include the matched pieces (they contain parts of the password).

Example: Weak: dictionary word + year + "!"

{
  "password": "Summer2024!"
}

Result

{
  "score": 1,
  "label": "Weak",
  "bits": 26.6,
  "guessesLog10": 8,
  "crackTimes": [
    {
      "attack": "Online, rate-limited (100 / hour)",
      "time": "1 century"
    },
    {
      "attack": "Online, no limit (10 / second)",
      "time": "4 months"
    },
    {
      "attack": "Offline, slow hash (10k / second, bcrypt)",
      "time": "3 hours"
    },
    {
      "attack": "Offline, fast hash (10B / second, GPU, MD5)",
      "time": "instant"
    }
  ],
  "tips": [
    "It contains a very common password. Attackers try those, and variations of them, first.",
    "Capitalizing only the first letter barely helps.",
    "Years and dates are easy to guess, especially birthdays and anniversaries.",
    "Ending with ! or 1 is the most common trick, so attackers expect it.",
    "Use at least 12–16 characters. Length beats complexity.",
    "Try a passphrase of 5–6 random words (a generator picks better than people do).",
    "Use a different password for every site. A password manager makes that painless."
  ],
  "stats": {
    "characters": 11,
    "characterTypes": 4,
    "piecesFound": 3
  },
  "pieces": [
    {
      "token": "Summer",
      "type": "common",
      "note": "common password #80",
      "bits": 7.3
    },
    {
      "token": "2024",
      "type": "year",
      "note": "year",
      "bits": 4.3
    },
    {
      "token": "!",
      "type": "bruteforce",
      "note": "random characters",
      "bits": 6.6
    }
  ]
}

Example: Strong passphrase (> 50 bits)

{
  "password": "correct horse battery staple"
}

Result

{
  "score": 4,
  "label": "Very strong",
  "bits": 126.2,
  "guessesLog10": 38,
  "crackTimes": [
    {
      "attack": "Online, rate-limited (100 / hour)",
      "time": "10^32 years"
    },
    {
      "attack": "Online, no limit (10 / second)",
      "time": "10^29 years"
    },
    {
      "attack": "Offline, slow hash (10k / second, bcrypt)",
      "time": "10^26 years"
    },
    {
      "attack": "Offline, fast hash (10B / second, GPU, MD5)",
      "time": "10^20 years"
    }
  ],
  "tips": [
    "Repeats like aaa or abcabc add very little over the part being repeated.",
    "Excellent. Keep it unique to one account so a leak elsewhere can't unlock it.",
    "Use a different password for every site. A password manager makes that painless."
  ],
  "stats": {
    "characters": 28,
    "characterTypes": 2,
    "piecesFound": 6
  },
  "pieces": [
    {
      "token": "correct",
      "type": "word",
      "note": "dictionary word",
      "bits": 14.3
    },
    {
      "token": " ",
      "type": "bruteforce",
      "note": "random characters",
      "bits": 5.9
    },
    {
      "token": "horse",
      "type": "word",
      "note": "dictionary word",
      "bits": 14.3
    },
    {
      "token": " ba",
      "type": "bruteforce",
      "note": "random characters",
      "bits": 17.6
    },
    {
      "token": "tt",
      "type": "repeat",
      "note": "repeat ×2",
      "bits": 5.8
    },
    {
      "token": "ery staple",
      "type": "bruteforce",
      "note": "random characters",
      "bits": 58.8
    }
  ]
}
password_generatePassword & passphrase generator

Generate strong random passwords (8–128 chars; lower/upper/digits/symbols; skip look-alikes Il1|O0o; exclude your own characters; require every chosen set) or passphrases (3–12 words from the website's 2,381-word list, separator, capitalise, add a number). Returns 1–1000 results with exact entropy bits, a strength grade and the time to crack at 100 billion guesses/s. Always crypto random (OS CSPRNG, rejection sampling); nothing is stored or logged. Web version: https://s3eidat.github.io/pass-forge/

InputTypeDescription
mode"password" | "passphrase"
default "password"
Random characters or random words.
lengthinteger
default 20
password: number of characters.
lowerboolean
default true
password: include a–z.
upperboolean
default true
password: include A–Z.
digitsboolean
default true
password: include 0–9.
symbolsboolean
default true
password: include symbols !#$%&*+-=?@^_~()[]{}<>.,:;/|
avoidLookalikesboolean
default true
password: leave out I l 1 | O 0 o.
requireEachSetboolean
default true
password: at least one character from every chosen set.
excludestring
default ""
password: characters never to use.
wordsinteger
default 6
passphrase: number of words.
separator"-" | " " | "." | "_" | ""
default "-"
passphrase: between words ("" = none, words then capitalised).
capitalizeboolean
default false
passphrase: capitalise each word.
addNumberboolean
default false
passphrase: append a number 0–99 to one random word.
countinteger
default 1
How many to generate.

Example: 20 chars from 83 possible, ≈127.3 bits, "Very strong"

{
  "length": 20
}

Example: e.g. "maple-orbit-velvet-harbor-pencil-river", ≈67.3 bits

{
  "mode": "passphrase",
  "words": 6,
  "separator": "-"
}
prayer_timesPrayer times

Islamic prayer times (Fajr, Sunrise, Dhuhr, Asr, Maghrib, Isha) for any coordinates + IANA time zone, or a known city, for one day, several days or a whole month, plus the Hijri (Umm al-Qura) date and the next prayer with a countdown. Same astronomy as the website (adhan.js): 13 methods (Jordan Awqaf, MWL, Umm al-Qura, Egyptian, ISNA, Karachi, Dubai, Kuwait, Qatar, Turkey, Singapore, Tehran, Moonsighting), Shafi'i or Hanafi Asr, and the recommended high-latitude rule beyond 48° (overridable). Times are local HH:MM (24 h) in the given zone, plus UTC ISO. Coordinates are rounded to 4 decimals like the site. Web version: https://s3eidat.github.io/prayer-times/

InputTypeDescription
citystringA known city (sets coordinates, time zone and its usual method): amman, irbid, zarqa, aqaba, mecca, medina, riyadh, jeddah, jerusalem, gaza, damascus, beirut, baghdad, cairo, alexandria, dubai, abudhabi, doha, kuwait, manama, muscat, istanbul, casablanca, tunis, algiers, khartoum, karachi, lahore, dhaka, delhi, kualalumpur, jakarta, tehran, london, paris, berlin, newyork, chicago, toronto, losangeles, sydney.
latitudenumberLatitude in degrees (−90..90). Use with longitude and timezone.
longitudenumberLongitude in degrees (−180..180).
timezonestringIANA time zone of the place, e.g. "Asia/Amman" (required with coordinates; overrides the city's).
datestringFirst day, YYYY-MM-DD in the place's calendar. Default: today there.
daysinteger
default 1
How many consecutive days to return.
monthstringOr a whole month timetable, YYYY-MM (overrides date/days).
method"jordan" | "mwl" | "ummalqura" | "egyptian" | "isna" | "karachi" | "dubai" | "kuwait" | "qatar" | "turkey" | "singapore" | "tehran" | "moonsighting"Calculation method. Default: the city's usual method, else "mwl".
asr"shafi" | "hanafi"Asr school: "shafi" (standard: Shafi'i, Maliki, Hanbali) or "hanafi". Default shafi (hanafi for Karachi-method cities).
highLatitudeRule"auto" | "middleofthenight" | "seventhofthenight" | "twilightangle"
default "auto"
"auto" = website behaviour (recommended rule when |latitude| > 48°).
nowstringReference instant (ISO 8601) for "today" and the next prayer. Default: the current time.

Example: Dhuhr 12:47 (09:47 UTC)

{
  "city": "amman",
  "date": "2026-01-15",
  "method": "mwl"
}

Result

{
  "place": "Amman",
  "latitude": 31.9539,
  "longitude": 35.9106,
  "timezone": "Asia/Amman",
  "method": {
    "id": "mwl",
    "name": "Muslim World League"
  },
  "asr": "shafi",
  "highLatitudeRule": null,
  "days": [
    {
      "date": "2026-01-15",
      "weekday": "Thursday",
      "hijri": "Rajab 26, 1447 AH",
      "hijriAr": "26 رجب 1447 هـ",
      "times": {
        "fajr": "06:11",
        "sunrise": "07:37",
        "dhuhr": "12:47",
        "asr": "15:36",
        "maghrib": "17:55",
        "isha": "19:16"
      },
      "utc": {
        "fajr": "2026-01-15T03:11:00.000Z",
        "sunrise": "2026-01-15T04:37:00.000Z",
        "dhuhr": "2026-01-15T09:47:00.000Z",
        "asr": "2026-01-15T12:36:00.000Z",
        "maghrib": "2026-01-15T14:55:00.000Z",
        "isha": "2026-01-15T16:16:00.000Z"
      }
    }
  ],
  "next": {
    "prayer": "dhuhr",
    "time": "12:47",
    "utc": "2026-01-15T09:47:00.000Z",
    "tomorrow": false,
    "countdown": "00:17:00",
    "minutes": 17
  }
}

Example: London monthly timetable, MWL, seventh-of-the-night rule

{
  "latitude": 51.5074,
  "longitude": -0.1278,
  "timezone": "Europe/London",
  "month": "2026-03"
}

Result

{
  "latitude": 51.5074,
  "longitude": -0.1278,
  "timezone": "Europe/London",
  "method": {
    "id": "mwl",
    "name": "Muslim World League"
  },
  "asr": "shafi",
  "highLatitudeRule": "seventhofthenight",
  "days": [
    {
      "date": "2026-03-01",
      "weekday": "Sunday",
      "hijri": "Ramadan 12, 1447 AH",
      "hijriAr": "12 رمضان 1447 هـ",
      "times": {
        "fajr": "04:55",
        "sunrise": "06:46",
        "dhuhr": "12:14",
        "asr": "15:05",
        "maghrib": "17:41",
        "isha": "19:25"
      },
      "utc": {
        "fajr": "2026-03-01T04:55:00.000Z",
        "sunrise": "2026-03-01T06:46:00.000Z",
        "dhuhr": "2026-03-01T12:14:00.000Z",
        "asr": "2026-03-01T15:05:00.000Z",
        "maghrib": "2026-03-01T17:41:00.000Z",
        "isha": "2026-03-01T19:25:00.000Z"
      }
    },
    {
      "date": "2026-03-02",
      "weekday": "Monday",
      "hijri": "Ramadan 13, 1447 AH",
      "hijriAr": "13 رمضان 1447 هـ",
      "times": {
        "fajr": "04:53",
        "sunrise": "06:44",
        "dhuhr": "12:14",
        "asr": "15:06",
        "maghrib": "17:42",
        "isha": "19:27"
      },
      "utc": {
        "fajr": "2026-03-02T04:53:00.000Z",
        "sunrise": "2026-03-02T06:44:00.000Z",
        "dhuhr": "2026-03-02T12:14:00.000Z",
        "asr": "2026-03-02T15:06:00.000Z",
        "maghrib": "2026-03-02T17:42:00.000Z",
        "isha": "2026-03-02T19:27:00.000Z"
      }
    },
    {
      "date": "2026-03-03",
      "weekday": "Tuesday",
      "hijri": "Ramadan 14, 1447 AH",
      "hijriAr": "14 رمضان 1447 هـ",
      "times": {
        "fajr": "04:51",
        "sunrise": "06:41",
        "dhuhr": "12:13",
        "asr": "15:07",
        "maghrib": "17:44",
        "isha": "19:29"
      },
      "utc": {
        "fajr": "2026-03-03T04:51:00.714Z",
        "sunrise": "2026-03-03T06:41:00.000Z",
        "dhuhr": "2026-03-03T12:13:00.000Z",
        "asr": "2026-03-03T15:07:00.000Z",
        "maghrib": "2026-03-03T17:44:00.000Z",
        "isha": "2026-03-03T19:29:00.000Z"
      }
    },
    {
      "date": "2026-03-04",
      "weekday": "Wednesday",
      "hijri": "Ramadan 15, 1447 AH",
      "hijriAr": "15 رمضان 1447 هـ",
      "times": {
        "fajr": "04:49",
        "sunrise": "06:39",
        "dhuhr": "12:13",
        "asr": "15:09",
        "maghrib": "17:46",
        "isha": "19:31"
      },
      "utc": {
        "fajr": "2026-03-04T04:49:00.571Z",
        "sunrise": "2026-03-04T06:39:00.000Z",
        "dhuhr": "2026-03-04T12:13:00.000Z",
        "asr": "2026-03-04T15:09:00.000Z",
        "maghrib": "2026-03-04T17:46:00.000Z",
        "isha": "2026-03-04T19:31:00.000Z"
      }
    },
    {
      "date": "2026-03-05",
      "weekday": "Thursday",
      "hijri": "Ramadan 16, 1447 AH",
      "hijriAr": "16 رمضان 1447 هـ",
      "times": {
        "fajr": "04:48",
        "sunrise": "06:37",
        "dhuhr": "12:13",
        "asr": "15:10",
        "maghrib": "17:48",
        "isha": "19:32"
      },
      "utc": {
        "fajr": "2026-03-05T04:48:00.428Z",
        "sunrise": "2026-03-05T06:37:00.000Z",
        "dhuhr": "2026-03-05T12:13:00.000Z",
        "asr": "2026-03-05T15:10:00.000Z",
        "maghrib": "2026-03-05T17:48:00.000Z",
        "isha": "2026-03-05T19:32:00.000Z"
      }
    },
    {
      "date": "2026-03-06",
      "weekday": "Friday",
      "hijri": "Ramadan 17, 1447 AH",
      "hijriAr": "17 رمضان 1447 هـ",
      "times": {
        "fajr": "04:46",
        "sunrise": "06:35",
        "dhuhr": "12:13",
        "asr": "15:11",
        "maghrib": "17:50",
        "isha": "19:34"
      },
      "utc": {
        "fajr": "2026-03-06T04:46:00.285Z",
        "sunrise": "2026-03-06T06:35:00.000Z",
        "dhuhr": "2026-03-06T12:13:00.000Z",
        "asr": "2026-03-06T15:11:00.000Z",
        "maghrib": "2026-03-06T17:50:00.000Z",
        "isha": "2026-03-06T19:34:00.000Z"
      }
    },
    {
      "date": "2026-03-07",
      "weekday": "Saturday",
      "hijri": "Ramadan 18, 1447 AH",
      "hijriAr": "18 رمضان 1447 هـ",
      "times": {
        "fajr": "04:44",
        "sunrise": "06:33",
        "dhuhr": "12:13",
        "asr": "15:12",
        "maghrib": "17:51",
        "isha": "19:36"
      },
      "utc": {
        "fajr": "2026-03-07T04:44:00.285Z",
        "sunrise": "2026-03-07T06:33:00.000Z",
        "dhuhr": "2026-03-07T12:13:00.000Z",
        "asr": "2026-03-07T15:12:00.000Z",
        "maghrib": "2026-03-07T17:51:00.000Z",
        "isha": "2026-03-07T19:36:00.000Z"
      }
    },
    {
      "date": "2026-03-08",
      "weekday": "Sunday",
      "hijri": "Ramadan 19, 1447 AH",
      "hijriAr": "19 رمضان 1447 هـ",
      "times": {
        "fajr": "04:43",
        "sunrise": "06:30",
        "dhuhr": "12:12",
        "asr": "15:13",
        "maghrib": "17:53",
        "isha": "19:38"
      },
      "utc": {
        "fajr": "2026-03-08T04:43:00.142Z",
        "sunrise": "2026-03-08T06:30:00.000Z",
        "dhuhr": "2026-03-08T12:12:00.000Z",
        "asr": "2026-03-08T15:13:00.000Z",
        "maghrib": "2026-03-08T17:53:00.000Z",
        "isha": "2026-03-08T19:38:00.000Z"
      }
    },
    {
      "date": "2026-03-09",
      "weekday": "Monday",
      "hijri": "Ramadan 20, 1447 AH",
      "hijriAr": "20 رمضان 1447 هـ",
      "times": {
        "fajr": "04:41",
        "sunrise": "06:28",
        "dhuhr": "12:12",
        "asr": "15:15",
        "maghrib": "17:55",
        "isha": "19:40"
      },
      "utc": {
        "fajr": "2026-03-09T04:41:00.285Z",
        "sunrise": "2026-03-09T06:28:00.000Z",
        "dhuhr": "2026-03-09T12:12:00.000Z",
        "asr": "2026-03-09T15:15:00.000Z",
        "maghrib": "2026-03-09T17:55:00.000Z",
        "isha": "2026-03-09T19:40:00.000Z"
      }
    },
    {
      "date": "2026-03-10",
      "weekday": "Tuesday",
      "hijri": "Ramadan 21, 1447 AH",
      "hijriAr": "21 رمضان 1447 هـ",
      "times": {
        "fajr": "04:39",
        "sunrise": "06:26",
        "dhuhr": "12:12",
        "asr": "15:16",
        "maghrib": "17:56",
        "isha": "19:42"
      },
      "utc": {
        "fajr": "2026-03-10T04:39:00.142Z",
        "sunrise": "2026-03-10T06:26:00.000Z",
        "dhuhr": "2026-03-10T12:12:00.000Z",
        "asr": "2026-03-10T15:16:00.000Z",
        "maghrib": "2026-03-10T17:56:00.000Z",
        "isha": "2026-03-10T19:42:00.000Z"
      }
    },
    {
      "date": "2026-03-11",
      "weekday": "Wednesday",
      "hijri": "Ramadan 22, 1447 AH",
      "hijriAr": "22 رمضان 1447 هـ",
      "times": {
        "fajr": "04:38",
        "sunrise": "06:24",
        "dhuhr": "12:12",
        "asr": "15:17",
        "maghrib": "17:58",
        "isha": "19:43"
      },
      "utc": {
        "fajr": "2026-03-11T04:38:00.285Z",
        "sunrise": "2026-03-11T06:24:00.000Z",
        "dhuhr": "2026-03-11T12:12:00.000Z",
        "asr": "2026-03-11T15:17:00.000Z",
        "maghrib": "2026-03-11T17:58:00.000Z",
        "isha": "2026-03-11T19:43:00.000Z"
      }
    },
    {
      "date": "2026-03-12",
      "weekday": "Thursday",
      "hijri": "Ramadan 23, 1447 AH",
      "hijriAr": "23 رمضان 1447 هـ",
      "times": {
        "fajr": "04:36",
        "sunrise": "06:22",
        "dhuhr": "12:11",
        "asr": "15:18",
        "maghrib": "18:00",
        "isha": "19:45"
      },
      "utc": {
        "fajr": "2026-03-12T04:36:00.285Z",
        "sunrise": "2026-03-12T06:22:00.000Z",
        "dhuhr": "2026-03-12T12:11:00.000Z",
        "asr": "2026-03-12T15:18:00.000Z",
        "maghrib": "2026-03-12T18:00:00.000Z",
        "isha": "2026-03-12T19:45:00.000Z"
      }
    },
    {
      "date": "2026-03-13",
      "weekday": "Friday",
      "hijri": "Ramadan 24, 1447 AH",
      "hijriAr": "24 رمضان 1447 هـ",
      "times": {
        "fajr": "04:34",
        "sunrise": "06:19",
        "dhuhr": "12:11",
        "asr": "15:19",
        "maghrib": "18:02",
        "isha": "19:47"
      },
      "utc": {
        "fajr": "2026-03-13T04:34:00.428Z",
        "sunrise": "2026-03-13T06:19:00.000Z",
        "dhuhr": "2026-03-13T12:11:00.000Z",
        "asr": "2026-03-13T15:19:00.000Z",
        "maghrib": "2026-03-13T18:02:00.000Z",
        "isha": "2026-03-13T19:47:00.571Z"
      }
    },
    {
      "date": "2026-03-14",
      "weekday": "Saturday",
      "hijri": "Ramadan 25, 1447 AH",
      "hijriAr": "25 رمضان 1447 هـ",
      "times": {
        "fajr": "04:33",
        "sunrise": "06:17",
        "dhuhr": "12:11",
        "asr": "15:20",
        "maghrib": "18:03",
        "isha": "19:48"
      },
      "utc": {
        "fajr": "2026-03-14T04:33:00.571Z",
        "sunrise": "2026-03-14T06:17:00.000Z",
        "dhuhr": "2026-03-14T12:11:00.000Z",
        "asr": "2026-03-14T15:20:00.000Z",
        "maghrib": "2026-03-14T18:03:00.000Z",
        "isha": "2026-03-14T19:48:00.428Z"
      }
    },
    {
      "date": "2026-03-15",
      "weekday": "Sunday",
      "hijri": "Ramadan 26, 1447 AH",
      "hijriAr": "26 رمضان 1447 هـ",
      "times": {
        "fajr": "04:31",
        "sunrise": "06:15",
        "dhuhr": "12:10",
        "asr": "15:21",
        "maghrib": "18:05",
        "isha": "19:49"
      },
      "utc": {
        "fajr": "2026-03-15T04:31:00.714Z",
        "sunrise": "2026-03-15T06:15:00.000Z",
        "dhuhr": "2026-03-15T12:10:00.000Z",
        "asr": "2026-03-15T15:21:00.000Z",
        "maghrib": "2026-03-15T18:05:00.000Z",
        "isha": "2026-03-15T19:49:00.285Z"
      }
    },
    {
      "date": "2026-03-16",
      "weekday": "Monday",
      "hijri": "Ramadan 27, 1447 AH",
      "hijriAr": "27 رمضان 1447 هـ",
      "times": {
        "fajr": "04:29",
        "sunrise": "06:12",
        "dhuhr": "12:10",
        "asr": "15:22",
        "maghrib": "18:07",
        "isha": "19:50"
      },
      "utc": {
        "fajr": "2026-03-16T04:29:00.714Z",
        "sunrise": "2026-03-16T06:12:00.000Z",
        "dhuhr": "2026-03-16T12:10:00.000Z",
        "asr": "2026-03-16T15:22:00.000Z",
        "maghrib": "2026-03-16T18:07:00.000Z",
        "isha": "2026-03-16T19:50:00.285Z"
      }
    },
    {
      "date": "2026-03-17",
      "weekday": "Tuesday",
      "hijri": "Ramadan 28, 1447 AH",
      "hijriAr": "28 رمضان 1447 هـ",
      "times": {
        "fajr": "04:27",
        "sunrise": "06:10",
        "dhuhr": "12:10",
        "asr": "15:23",
        "maghrib": "18:08",
        "isha": "19:51"
      },
      "utc": {
        "fajr": "2026-03-17T04:27:00.714Z",
        "sunrise": "2026-03-17T06:10:00.000Z",
        "dhuhr": "2026-03-17T12:10:00.000Z",
        "asr": "2026-03-17T15:23:00.000Z",
        "maghrib": "2026-03-17T18:08:00.000Z",
        "isha": "2026-03-17T19:51:00.285Z"
      }
    },
    {
      "date": "2026-03-18",
      "weekday": "Wednesday",
      "hijri": "Ramadan 29, 1447 AH",
      "hijriAr": "29 رمضان 1447 هـ",
      "times": {
        "fajr": "04:26",
        "sunrise": "06:08",
        "dhuhr": "12:10",
        "asr": "15:25",
        "maghrib": "18:10",
        "isha": "19:52"
      },
      "utc": {
        "fajr": "2026-03-18T04:26:00.857Z",
        "sunrise": "2026-03-18T06:08:00.000Z",
        "dhuhr": "2026-03-18T12:10:00.000Z",
        "asr": "2026-03-18T15:25:00.000Z",
        "maghrib": "2026-03-18T18:10:00.000Z",
        "isha": "2026-03-18T19:52:00.142Z"
      }
    },
    {
      "date": "2026-03-19",
      "weekday": "Thursday",
      "hijri": "Ramadan 30, 1447 AH",
      "hijriAr": "30 رمضان 1447 هـ",
      "times": {
        "fajr": "04:24",
        "sunrise": "06:06",
        "dhuhr": "12:09",
        "asr": "15:26",
        "maghrib": "18:12",
        "isha": "19:53"
      },
      "utc": {
        "fajr": "2026-03-19T04:24:00.000Z",
        "sunrise": "2026-03-19T06:06:00.000Z",
        "dhuhr": "2026-03-19T12:09:00.000Z",
        "asr": "2026-03-19T15:26:00.000Z",
        "maghrib": "2026-03-19T18:12:00.000Z",
        "isha": "2026-03-19T19:53:00.000Z"
      }
    },
    {
      "date": "2026-03-20",
      "weekday": "Friday",
      "hijri": "Shawwal 1, 1447 AH",
      "hijriAr": "1 شوال 1447 هـ",
      "times": {
        "fajr": "04:22",
        "sunrise": "06:03",
        "dhuhr": "12:09",
        "asr": "15:27",
        "maghrib": "18:14",
        "isha": "19:55"
      },
      "utc": {
        "fajr": "2026-03-20T04:22:00.142Z",
        "sunrise": "2026-03-20T06:03:00.000Z",
        "dhuhr": "2026-03-20T12:09:00.000Z",
        "asr": "2026-03-20T15:27:00.000Z",
        "maghrib": "2026-03-20T18:14:00.000Z",
        "isha": "2026-03-20T19:55:00.857Z"
      }
    },
    {
      "date": "2026-03-21",
      "weekday": "Saturday",
      "hijri": "Shawwal 2, 1447 AH",
      "hijriAr": "2 شوال 1447 هـ",
      "times": {
        "fajr": "04:21",
        "sunrise": "06:01",
        "dhuhr": "12:09",
        "asr": "15:28",
        "maghrib": "18:15",
        "isha": "19:56"
      },
      "utc": {
        "fajr": "2026-03-21T04:21:00.285Z",
        "sunrise": "2026-03-21T06:01:00.000Z",
        "dhuhr": "2026-03-21T12:09:00.000Z",
        "asr": "2026-03-21T15:28:00.000Z",
        "maghrib": "2026-03-21T18:15:00.000Z",
        "isha": "2026-03-21T19:56:00.714Z"
      }
    },
    {
      "date": "2026-03-22",
      "weekday": "Sunday",
      "hijri": "Shawwal 3, 1447 AH",
      "hijriAr": "3 شوال 1447 هـ",
      "times": {
        "fajr": "04:19",
        "sunrise": "05:59",
        "dhuhr": "12:08",
        "asr": "15:29",
        "maghrib": "18:17",
        "isha": "19:57"
      },
      "utc": {
        "fajr": "2026-03-22T04:19:00.285Z",
        "sunrise": "2026-03-22T05:59:00.000Z",
        "dhuhr": "2026-03-22T12:08:00.000Z",
        "asr": "2026-03-22T15:29:00.000Z",
        "maghrib": "2026-03-22T18:17:00.000Z",
        "isha": "2026-03-22T19:57:00.714Z"
      }
    },
    {
      "date": "2026-03-23",
      "weekday": "Monday",
      "hijri": "Shawwal 4, 1447 AH",
      "hijriAr": "4 شوال 1447 هـ",
      "times": {
        "fajr": "04:17",
        "sunrise": "05:57",
        "dhuhr": "12:08",
        "asr": "15:30",
        "maghrib": "18:19",
        "isha": "19:58"
      },
      "utc": {
        "fajr": "2026-03-23T04:17:00.428Z",
        "sunrise": "2026-03-23T05:57:00.000Z",
        "dhuhr": "2026-03-23T12:08:00.000Z",
        "asr": "2026-03-23T15:30:00.000Z",
        "maghrib": "2026-03-23T18:19:00.000Z",
        "isha": "2026-03-23T19:58:00.571Z"
      }
    },
    {
      "date": "2026-03-24",
      "weekday": "Tuesday",
      "hijri": "Shawwal 5, 1447 AH",
      "hijriAr": "5 شوال 1447 هـ",
      "times": {
        "fajr": "04:15",
        "sunrise": "05:54",
        "dhuhr": "12:08",
        "asr": "15:31",
        "maghrib": "18:20",
        "isha": "19:59"
      },
      "utc": {
        "fajr": "2026-03-24T04:15:00.428Z",
        "sunrise": "2026-03-24T05:54:00.000Z",
        "dhuhr": "2026-03-24T12:08:00.000Z",
        "asr": "2026-03-24T15:31:00.000Z",
        "maghrib": "2026-03-24T18:20:00.000Z",
        "isha": "2026-03-24T19:59:00.571Z"
      }
    },
    {
      "date": "2026-03-25",
      "weekday": "Wednesday",
      "hijri": "Shawwal 6, 1447 AH",
      "hijriAr": "6 شوال 1447 هـ",
      "times": {
        "fajr": "04:14",
        "sunrise": "05:52",
        "dhuhr": "12:07",
        "asr": "15:32",
        "maghrib": "18:22",
        "isha": "20:00"
      },
      "utc": {
        "fajr": "2026-03-25T04:14:00.285Z",
        "sunrise": "2026-03-25T05:52:00.000Z",
        "dhuhr": "2026-03-25T12:07:00.000Z",
        "asr": "2026-03-25T15:32:00.000Z",
        "maghrib": "2026-03-25T18:22:00.000Z",
        "isha": "2026-03-25T20:00:00.714Z"
      }
    },
    {
      "date": "2026-03-26",
      "weekday": "Thursday",
      "hijri": "Shawwal 7, 1447 AH",
      "hijriAr": "7 شوال 1447 هـ",
      "times": {
        "fajr": "04:12",
        "sunrise": "05:50",
        "dhuhr": "12:07",
        "asr": "15:32",
        "maghrib": "18:24",
        "isha": "20:01"
      },
      "utc": {
        "fajr": "2026-03-26T04:12:00.285Z",
        "sunrise": "2026-03-26T05:50:00.000Z",
        "dhuhr": "2026-03-26T12:07:00.000Z",
        "asr": "2026-03-26T15:32:00.000Z",
        "maghrib": "2026-03-26T18:24:00.000Z",
        "isha": "2026-03-26T20:01:00.714Z"
      }
    },
    {
      "date": "2026-03-27",
      "weekday": "Friday",
      "hijri": "Shawwal 8, 1447 AH",
      "hijriAr": "8 شوال 1447 هـ",
      "times": {
        "fajr": "04:10",
        "sunrise": "05:47",
        "dhuhr": "12:07",
        "asr": "15:33",
        "maghrib": "18:25",
        "isha": "20:02"
      },
      "utc": {
        "fajr": "2026-03-27T04:10:00.285Z",
        "sunrise": "2026-03-27T05:47:00.000Z",
        "dhuhr": "2026-03-27T12:07:00.000Z",
        "asr": "2026-03-27T15:33:00.000Z",
        "maghrib": "2026-03-27T18:25:00.000Z",
        "isha": "2026-03-27T20:02:00.714Z"
      }
    },
    {
      "date": "2026-03-28",
      "weekday": "Saturday",
      "hijri": "Shawwal 9, 1447 AH",
      "hijriAr": "9 شوال 1447 هـ",
      "times": {
        "fajr": "04:09",
        "sunrise": "05:45",
        "dhuhr": "12:07",
        "asr": "15:34",
        "maghrib": "18:27",
        "isha": "20:04"
      },
      "utc": {
        "fajr": "2026-03-28T04:09:00.285Z",
        "sunrise": "2026-03-28T05:45:00.000Z",
        "dhuhr": "2026-03-28T12:07:00.000Z",
        "asr": "2026-03-28T15:34:00.000Z",
        "maghrib": "2026-03-28T18:27:00.000Z",
        "isha": "2026-03-28T20:04:00.714Z"
      }
    },
    {
      "date": "2026-03-29",
      "weekday": "Sunday",
      "hijri": "Shawwal 10, 1447 AH",
      "hijriAr": "10 شوال 1447 هـ",
      "times": {
        "fajr": "05:07",
        "sunrise": "06:43",
        "dhuhr": "13:06",
        "asr": "16:35",
        "maghrib": "19:29",
        "isha": "21:05"
      },
      "utc": {
        "fajr": "2026-03-29T04:07:00.000Z",
        "sunrise": "2026-03-29T05:43:00.000Z",
        "dhuhr": "2026-03-29T12:06:00.000Z",
        "asr": "2026-03-29T15:35:00.000Z",
        "maghrib": "2026-03-29T18:29:00.000Z",
        "isha": "2026-03-29T20:05:00.000Z"
      }
    },
    {
      "date": "2026-03-30",
      "weekday": "Monday",
      "hijri": "Shawwal 11, 1447 AH",
      "hijriAr": "11 شوال 1447 هـ",
      "times": {
        "fajr": "05:05",
        "sunrise": "06:41",
        "dhuhr": "13:06",
        "asr": "16:36",
        "maghrib": "19:30",
        "isha": "21:06"
      },
      "utc": {
        "fajr": "2026-03-30T04:05:00.857Z",
        "sunrise": "2026-03-30T05:41:00.000Z",
        "dhuhr": "2026-03-30T12:06:00.000Z",
        "asr": "2026-03-30T15:36:00.000Z",
        "maghrib": "2026-03-30T18:30:00.000Z",
        "isha": "2026-03-30T20:06:00.142Z"
      }
    },
    {
      "date": "2026-03-31",
      "weekday": "Tuesday",
      "hijri": "Shawwal 12, 1447 AH",
      "hijriAr": "12 شوال 1447 هـ",
      "times": {
        "fajr": "05:03",
        "sunrise": "06:38",
        "dhuhr": "13:06",
        "asr": "16:37",
        "maghrib": "19:32",
        "isha": "21:07"
      },
      "utc": {
        "fajr": "2026-03-31T04:03:00.714Z",
        "sunrise": "2026-03-31T05:38:00.000Z",
        "dhuhr": "2026-03-31T12:06:00.000Z",
        "asr": "2026-03-31T15:37:00.000Z",
        "maghrib": "2026-03-31T18:32:00.000Z",
        "isha": "2026-03-31T20:07:00.285Z"
      }
    }
  ],
  "next": {
    "prayer": "dhuhr",
    "time": "12:11",
    "utc": "2026-01-15T12:11:00.000Z",
    "tomorrow": false,
    "countdown": "02:41:00",
    "minutes": 161
  }
}
qibla_directionQibla direction

Qibla direction (great-circle bearing from true north to the Kaaba in Makkah) and distance from any coordinates or a known city, with the compass point, a "face north, then turn right/left N°" instruction, and (if you give a device heading) how far to turn. Bearings are from TRUE north; a magnetic compass can differ by a few degrees. Web version: https://s3eidat.github.io/qibla-finder/

InputTypeDescription
latitudenumberLatitude in degrees (−90..90).
longitudenumberLongitude in degrees (−180..180).
citystringOr a known city: amman, irbid, zarqa, aqaba, mecca, medina, riyadh, jeddah, jerusalem, gaza, damascus, beirut, baghdad, cairo, alexandria, dubai, abudhabi, doha, kuwait, manama, muscat, istanbul, casablanca, tunis, algiers, khartoum, karachi, lahore, dhaka, delhi, kualalumpur, jakarta, tehran, london, paris, berlin, newyork, chicago, toronto, losangeles, sydney.
headingnumberOptional: the direction you are facing now (degrees from north) to get a turn instruction.

Example: Face north, then turn clockwise 160.7°

{
  "city": "amman"
}

Result

{
  "bearing": 160.7,
  "direction": "SSE",
  "distanceKm": 1234
}

Example: London: about 119° (ESE)

{
  "latitude": 51.5074,
  "longitude": -0.1278
}

Result

{
  "latitude": 51.5074,
  "longitude": -0.1278,
  "bearing": 119,
  "direction": "ESE",
  "directionAr": "جنوب شرق",
  "distanceKm": 4794,
  "atKaaba": false,
  "turnFromNorth": {
    "direction": "right",
    "degrees": 119
  },
  "instruction": "Face north, then turn clockwise (to your right) by 119°. You are then facing the Qibla (ESE)."
}
random_diceDice roller, coin flip, random numbers

Roll dice with standard notation (2d6+3, d20, d%, 4d6dl1 / "4d6 drop lowest", 2d20kh1 advantage, 2d20kl1, 5d10dh2; up to 100 dice per term, 2–1000 sides, 20 terms), flip coins (counts + streaks), or draw random whole numbers in a range (optionally unique and sorted, up to 10,000). Crypto random with rejection sampling (no modulo bias); optional seed for reproducible results. Web version: https://s3eidat.github.io/dice-roller/

InputTypeDescription
mode"dice" | "coin" | "numbers"
default "dice"
What to do.
notationstring
default "2d6"
dice: the dice notation, e.g. "3d8+2d6+4".
rollsinteger
default 1
dice: how many times to roll the expression.
flipsinteger
default 1
coin: how many coins to flip.
mininteger
default 1
numbers: smallest value (inclusive).
maxinteger
default 100
numbers: largest value (inclusive).
countinteger
default 1
numbers: how many numbers.
uniqueboolean
default false
numbers: no repeats.
sortboolean
default false
numbers: sort ascending.
seedstringOptional seed for a reproducible result (same seed + same input = same output). Omit for true crypto randomness.

Example: e.g. total 13, breakdown "4d6dl1 [3, (1 dropped), 4, 6] = 13"

{
  "notation": "4d6 drop lowest"
}

Example

{
  "mode": "coin",
  "flips": 10
}

Example: Lottery-style draw

{
  "mode": "numbers",
  "min": 1,
  "max": 49,
  "count": 6,
  "unique": true,
  "sort": true
}
random_pickWheel picker (random pick)

Pick a random winner (or several, without repeats, like spinning with "remove winner" on) from a list of names, or shuffle / sort the whole list. Fair: crypto random with rejection sampling. Entries are trimmed, cut to 100 characters, max 500; at least 2 needed. Optional seed for a reproducible draw. Web version: https://s3eidat.github.io/wheel-picker/

InputTypeDescription
itemsstring[]The entries, one per array item.
textstringOr: the entries as text, one per line.
mode"pick" | "shuffle" | "sort"
default "pick"
"pick" winners, "shuffle" the list, or "sort" it A→Z.
countinteger
default 1
How many winners to pick (each winner is removed before the next spin).
seedstringOptional seed for a reproducible result (same seed + same input = same output). Omit for true crypto randomness.

Example: One random winner, e.g. { winners: [{ name: "Omar", index: 2 }] }

{
  "items": [
    "Ali",
    "Sara",
    "Omar",
    "Lina"
  ]
}

Example: Three distinct winners, reproducible

{
  "text": "Ali\nSara\nOmar\nLina\nHuda",
  "count": 3,
  "seed": "raffle-2026"
}
random_teamsTeam maker

Split a list of people into fair random teams: choose the number of teams or people per team. Add a skill level ("Ali, 5" / "Sara: 3") and teams are balanced by total skill (people without one count as the average). Team names: numbers, letters, colours, animals or your own. Crypto-random shuffle; optional seed for a reproducible split. Max 2000 people, 100 teams. Web version: https://s3eidat.github.io/team-maker/

InputTypeDescription
peoplestring[]People, one per item, optionally "Name, skill" (skill 0–100).
textstringOr: people as text, one per line.
mode"teams" | "per"
default "teams"
"teams": make `num` teams; "per": `num` people per team.
numinteger
default 2
Number of teams (min 2) or people per team (min 1), see mode.
balanceboolean
default true
Balance teams by skill when skills are given.
naming"num" | "letters" | "colors" | "animals" | "custom"
default "num"
Team names: Team 1…, Team A…, colours, animals, or customNames.
customNamesstring[]naming=custom: team names in order (missing ones fall back to "Team N").
seedstringOptional seed for a reproducible result (same seed + same input = same output). Omit for true crypto randomness.

Example: Two balanced teams (Lions, Falcons)

{
  "people": [
    "Ali, 5",
    "Sara, 4",
    "Omar, 3",
    "Lina, 5",
    "Yousef, 2",
    "Noor, 4"
  ],
  "num": 2,
  "naming": "animals"
}
secret_santa_drawSecret Santa draw

Draw Secret Santa (gift exchange) pairs: everyone gives to exactly one other person and receives from one, never themselves. Exclusions (couples, housemates…) block a pair both ways. Checks exactly whether a draw is possible and explains why not (and which exclusion to drop). Crypto random and uniform; optional seed. Up to 200 people. Nothing is emailed: hand each giver only their own line. Optional budget, date and message are echoed for slips. Web version: https://s3eidat.github.io/secret-santa/

InputTypeDescription
names requiredstring[]Participants (an item may also hold several names separated by commas or new lines).
exclusionsany[][]
default []
Pairs of names who must not draw each other, e.g. [["Omar","Sara"]].
budgetstringOptional gift budget, e.g. "20 JD".
datestringOptional exchange date, YYYY-MM-DD.
messagestringOptional note for everyone.
seedstringOptional seed for a reproducible result (same seed + same input = same output). Omit for true crypto randomness.

Example: Omar and Sara never draw each other

{
  "names": [
    "Omar",
    "Sara",
    "Lina",
    "Yousef",
    "Huda"
  ],
  "exclusions": [
    [
      "Omar",
      "Sara"
    ]
  ],
  "budget": "20 JD"
}
time_world_clockWorld clock & meeting planner

Current (or any given) time in a list of time zones or cities: local time, date, weekday, UTC offset, difference from a home zone, day/night and working-hours flags. Plus the website's meeting planner: the 24 hours of a day in the home zone, each zone's local hour and band (work 9–17, awake 7–22, night), and the hours when everyone is in working hours. Zones: IANA ids ("Asia/Tokyo") or city names ("Mecca", "New York", "Tokyo"). Uses Node's ICU time-zone data; no network. Web version: https://s3eidat.github.io/world-clock/

InputTypeDescription
zones requiredstring[]Time zones or cities, e.g. ["Asia/Amman", "London", "New York"]. The first is the home zone unless homeZone is set.
homeZonestringZone the differences and the planner day are measured from. Default: the first of zones.
atstringThe instant to show (ISO 8601, e.g. "2026-01-15T12:00:00Z"). Default: now.
hour12boolean
default false
12-hour clock (AM/PM) instead of 24-hour.
plannerboolean
default true
Include the 24-hour meeting planner.
plannerDatestringPlanner day (YYYY-MM-DD in the home zone). Default: the date of "at" there.

Example: Working-hours overlap 14:00–16:00 UTC (hours 14, 15, 16)

{
  "zones": [
    "Europe/London",
    "America/New_York"
  ],
  "at": "2026-01-15T12:00:00Z"
}

Result

{
  "at": "2026-01-15T12:00:00.000Z",
  "homeZone": "Europe/London",
  "clocks": [
    {
      "zone": "Europe/London",
      "city": "London",
      "region": "Europe",
      "time": "12:00:00",
      "date": "2026-01-15",
      "weekday": "Thu",
      "offset": "UTC",
      "offsetMinutes": 0,
      "diff": "home",
      "isDay": true,
      "band": "work",
      "workingHours": true
    },
    {
      "zone": "America/New_York",
      "city": "New York",
      "region": "America",
      "time": "07:00:00",
      "date": "2026-01-15",
      "weekday": "Thu",
      "offset": "UTC−5",
      "offsetMinutes": -300,
      "diff": "−5h",
      "isDay": true,
      "band": "awake",
      "workingHours": false
    }
  ],
  "planner": {
    "date": "2026-01-15",
    "startUtc": "2026-01-15T00:00:00.000Z",
    "overlapHours": [
      14,
      15,
      16
    ],
    "overlapUtc": [
      "2026-01-15T14:00:00.000Z",
      "2026-01-15T15:00:00.000Z",
      "2026-01-15T16:00:00.000Z"
    ],
    "summary": "Everyone is in working hours for 3 hours.",
    "rows": [
      {
        "zone": "Europe/London",
        "city": "London",
        "hours": [
          {
            "label": "0",
            "band": "night"
          },
          {
            "label": "1",
            "band": "night"
          },
          {
            "label": "2",
            "band": "night"
          },
          {
            "label": "3",
            "band": "night"
          },
          {
            "label": "4",
            "band": "night"
          },
          {
            "label": "5",
            "band": "night"
          },
          {
            "label": "6",
            "band": "night"
          },
          {
            "label": "7",
            "band": "awake"
          },
          {
            "label": "8",
            "band": "awake"
          },
          {
            "label": "9",
            "band": "work"
          },
          {
            "label": "10",
            "band": "work"
          },
          {
            "label": "11",
            "band": "work"
          },
          {
            "label": "12",
            "band": "work"
          },
          {
            "label": "13",
            "band": "work"
          },
          {
            "label": "14",
            "band": "work"
          },
          {
            "label": "15",
            "band": "work"
          },
          {
            "label": "16",
            "band": "work"
          },
          {
            "label": "17",
            "band": "awake"
          },
          {
            "label": "18",
            "band": "awake"
          },
          {
            "label": "19",
            "band": "awake"
          },
          {
            "label": "20",
            "band": "awake"
          },
          {
            "label": "21",
            "band": "awake"
          },
          {
            "label": "22",
            "band": "night"
          },
          {
            "label": "23",
            "band": "night"
          }
        ]
      },
      {
        "zone": "America/New_York",
        "city": "New York",
        "hours": [
          {
            "label": "19",
            "band": "awake"
          },
          {
            "label": "20",
            "band": "awake"
          },
          {
            "label": "21",
            "band": "awake"
          },
          {
            "label": "22",
            "band": "night"
          },
          {
            "label": "23",
            "band": "night"
          },
          {
            "label": "1/15",
            "band": "night"
          },
          {
            "label": "1",
            "band": "night"
          },
          {
            "label": "2",
            "band": "night"
          },
          {
            "label": "3",
            "band": "night"
          },
          {
            "label": "4",
            "band": "night"
          },
          {
            "label": "5",
            "band": "night"
          },
          {
            "label": "6",
            "band": "night"
          },
          {
            "label": "7",
            "band": "awake"
          },
          {
            "label": "8",
            "band": "awake"
          },
          {
            "label": "9",
            "band": "work"
          },
          {
            "label": "10",
            "band": "work"
          },
          {
            "label": "11",
            "band": "work"
          },
          {
            "label": "12",
            "band": "work"
          },
          {
            "label": "13",
            "band": "work"
          },
          {
            "label": "14",
            "band": "work"
          },
          {
            "label": "15",
            "band": "work"
          },
          {
            "label": "16",
            "band": "work"
          },
          {
            "label": "17",
            "band": "awake"
          },
          {
            "label": "18",
            "band": "awake"
          }
        ]
      }
    ]
  }
}

Example

{
  "zones": [
    "Asia/Amman",
    "Mecca",
    "Tokyo",
    "Los Angeles"
  ],
  "hour12": true
}

Result

{
  "at": "2026-01-15T09:30:00.000Z",
  "homeZone": "Asia/Amman",
  "clocks": [
    {
      "zone": "Asia/Amman",
      "city": "Amman",
      "region": "Asia",
      "time": "12:30:00 PM",
      "date": "2026-01-15",
      "weekday": "Thu",
      "offset": "UTC+3",
      "offsetMinutes": 180,
      "diff": "home",
      "isDay": true,
      "band": "work",
      "workingHours": true
    },
    {
      "zone": "Asia/Riyadh",
      "city": "Riyadh",
      "region": "Asia",
      "time": "12:30:00 PM",
      "date": "2026-01-15",
      "weekday": "Thu",
      "offset": "UTC+3",
      "offsetMinutes": 180,
      "diff": "same time",
      "isDay": true,
      "band": "work",
      "workingHours": true
    },
    {
      "zone": "Asia/Tokyo",
      "city": "Tokyo",
      "region": "Asia",
      "time": "6:30:00 PM",
      "date": "2026-01-15",
      "weekday": "Thu",
      "offset": "UTC+9",
      "offsetMinutes": 540,
      "diff": "+6h",
      "isDay": false,
      "band": "awake",
      "workingHours": false
    },
    {
      "zone": "America/Los_Angeles",
      "city": "Los Angeles",
      "region": "America",
      "time": "1:30:00 AM",
      "date": "2026-01-15",
      "weekday": "Thu",
      "offset": "UTC−8",
      "offsetMinutes": -480,
      "diff": "−11h",
      "isDay": false,
      "band": "night",
      "workingHours": false
    }
  ],
  "planner": {
    "date": "2026-01-15",
    "startUtc": "2026-01-14T21:00:00.000Z",
    "overlapHours": [],
    "overlapUtc": [],
    "summary": "No hour where everyone is inside 9–17. Look for the 'awake' hours instead.",
    "rows": [
      {
        "zone": "Asia/Amman",
        "city": "Amman",
        "hours": [
          {
            "label": "12a",
            "band": "night"
          },
          {
            "label": "1a",
            "band": "night"
          },
          {
            "label": "2a",
            "band": "night"
          },
          {
            "label": "3a",
            "band": "night"
          },
          {
            "label": "4a",
            "band": "night"
          },
          {
            "label": "5a",
            "band": "night"
          },
          {
            "label": "6a",
            "band": "night"
          },
          {
            "label": "7a",
            "band": "awake"
          },
          {
            "label": "8a",
            "band": "awake"
          },
          {
            "label": "9a",
            "band": "work"
          },
          {
            "label": "10a",
            "band": "work"
          },
          {
            "label": "11a",
            "band": "work"
          },
          {
            "label": "12p",
            "band": "work"
          },
          {
            "label": "1p",
            "band": "work"
          },
          {
            "label": "2p",
            "band": "work"
          },
          {
            "label": "3p",
            "band": "work"
          },
          {
            "label": "4p",
            "band": "work"
          },
          {
            "label": "5p",
            "band": "awake"
          },
          {
            "label": "6p",
            "band": "awake"
          },
          {
            "label": "7p",
            "band": "awake"
          },
          {
            "label": "8p",
            "band": "awake"
          },
          {
            "label": "9p",
            "band": "awake"
          },
          {
            "label": "10p",
            "band": "night"
          },
          {
            "label": "11p",
            "band": "night"
          }
        ]
      },
      {
        "zone": "Asia/Riyadh",
        "city": "Riyadh",
        "hours": [
          {
            "label": "12a",
            "band": "night"
          },
          {
            "label": "1a",
            "band": "night"
          },
          {
            "label": "2a",
            "band": "night"
          },
          {
            "label": "3a",
            "band": "night"
          },
          {
            "label": "4a",
            "band": "night"
          },
          {
            "label": "5a",
            "band": "night"
          },
          {
            "label": "6a",
            "band": "night"
          },
          {
            "label": "7a",
            "band": "awake"
          },
          {
            "label": "8a",
            "band": "awake"
          },
          {
            "label": "9a",
            "band": "work"
          },
          {
            "label": "10a",
            "band": "work"
          },
          {
            "label": "11a",
            "band": "work"
          },
          {
            "label": "12p",
            "band": "work"
          },
          {
            "label": "1p",
            "band": "work"
          },
          {
            "label": "2p",
            "band": "work"
          },
          {
            "label": "3p",
            "band": "work"
          },
          {
            "label": "4p",
            "band": "work"
          },
          {
            "label": "5p",
            "band": "awake"
          },
          {
            "label": "6p",
            "band": "awake"
          },
          {
            "label": "7p",
            "band": "awake"
          },
          {
            "label": "8p",
            "band": "awake"
          },
          {
            "label": "9p",
            "band": "awake"
          },
          {
            "label": "10p",
            "band": "night"
          },
          {
            "label": "11p",
            "band": "night"
          }
        ]
      },
      {
        "zone": "Asia/Tokyo",
        "city": "Tokyo",
        "hours": [
          {
            "label": "6a",
            "band": "night"
          },
          {
            "label": "7a",
            "band": "awake"
          },
          {
            "label": "8a",
            "band": "awake"
          },
          {
            "label": "9a",
            "band": "work"
          },
          {
            "label": "10a",
            "band": "work"
          },
          {
            "label": "11a",
            "band": "work"
          },
          {
            "label": "12p",
            "band": "work"
          },
          {
            "label": "1p",
            "band": "work"
          },
          {
            "label": "2p",
            "band": "work"
          },
          {
            "label": "3p",
            "band": "work"
          },
          {
            "label": "4p",
            "band": "work"
          },
          {
            "label": "5p",
            "band": "awake"
          },
          {
            "label": "6p",
            "band": "awake"
          },
          {
            "label": "7p",
            "band": "awake"
          },
          {
            "label": "8p",
            "band": "awake"
          },
          {
            "label": "9p",
            "band": "awake"
          },
          {
            "label": "10p",
            "band": "night"
          },
          {
            "label": "11p",
            "band": "night"
          },
          {
            "label": "1/16",
            "band": "night"
          },
          {
            "label": "1a",
            "band": "night"
          },
          {
            "label": "2a",
            "band": "night"
          },
          {
            "label": "3a",
            "band": "night"
          },
          {
            "label": "4a",
            "band": "night"
          },
          {
            "label": "5a",
            "band": "night"
          }
        ]
      },
      {
        "zone": "America/Los_Angeles",
        "city": "Los Angeles",
        "hours": [
          {
            "label": "1p",
            "band": "work"
          },
          {
            "label": "2p",
            "band": "work"
          },
          {
            "label": "3p",
            "band": "work"
          },
          {
            "label": "4p",
            "band": "work"
          },
          {
            "label": "5p",
            "band": "awake"
          },
          {
            "label": "6p",
            "band": "awake"
          },
          {
            "label": "7p",
            "band": "awake"
          },
          {
            "label": "8p",
            "band": "awake"
          },
          {
            "label": "9p",
            "band": "awake"
          },
          {
            "label": "10p",
            "band": "night"
          },
          {
            "label": "11p",
            "band": "night"
          },
          {
            "label": "1/15",
            "band": "night"
          },
          {
            "label": "1a",
            "band": "night"
          },
          {
            "label": "2a",
            "band": "night"
          },
          {
            "label": "3a",
            "band": "night"
          },
          {
            "label": "4a",
            "band": "night"
          },
          {
            "label": "5a",
            "band": "night"
          },
          {
            "label": "6a",
            "band": "night"
          },
          {
            "label": "7a",
            "band": "awake"
          },
          {
            "label": "8a",
            "band": "awake"
          },
          {
            "label": "9a",
            "band": "work"
          },
          {
            "label": "10a",
            "band": "work"
          },
          {
            "label": "11a",
            "band": "work"
          },
          {
            "label": "12p",
            "band": "work"
          }
        ]
      }
    ]
  }
}

QR & barcodes

Make and read QR codes, Wi-Fi QR, barcodes.

barcode_generateBarcode generator

Make 1D barcodes as SVG or PNG: Code 128 (any printable ASCII; digit runs packed with subset C automatically), EAN-13 (12 digits, check digit added; or 13, check digit verified), UPC-A (11 or 12 digits, same) and Code 39 (A-Z, 0-9, space, - . $ / + %, optional mod-43 check). Same encoders, quiet zones, guard bars and human-readable digits as the website. Give "value" for one barcode, or "values" (up to 1000) to write one file per value into a folder; invalid values are reported, not fatal. PNG text uses the system monospace font. Web version: https://s3eidat.github.io/barcode-gen/

InputTypeDescription
valuestringThe value to encode (one barcode).
valuesstring[]Or: many values; each becomes its own file in the output folder.
symbology"code128" | "ean13" | "upca" | "code39"
default "code128"
Barcode type (default code128).
code39Checkboolean
default false
Code 39 only: append the mod-43 check character.
format"svg" | "png"
default "svg"
Output format (default svg).
barWidthinteger
default 2
Narrow bar (module) width in px, 1-6 (default 2).
heightinteger
default 80
Bar height in px, 30-220 (default 80).
showTextboolean
default true
Print the human-readable text/digits under the bars.
foregroundstring
default "#000000"
Bar colour, hex.
backgroundstring
default "#ffffff"
Background colour, hex.
scaleinteger
default 2
PNG only: pixel scale (1×, 2× default, 4×…).
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example

{
  "value": "400638133393",
  "symbology": "ean13"
}

Result

{
  "text": "4006381333931",
  "note": "Check digit 1 added"
}

Example: Code 128 SVG

{
  "value": "S3EIDAT-2026"
}

Result

{
  "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-npITvj\\S3EIDAT-2026.svg",
  "bytes": 2214,
  "mimeType": "image/svg+xml",
  "text": "S3EIDAT-2026",
  "width": 352,
  "height": 123,
  "modules": 156
}

Example: Two files, one error (12345 is too short).

{
  "values": [
    "590123412345",
    "400638133393",
    "12345"
  ],
  "symbology": "ean13",
  "format": "png"
}

Result

{
  "folder": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-dH0Pbu\\barcodes-ean13\\",
  "files": [
    {
      "value": "590123412345",
      "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-dH0Pbu\\barcodes-ean13\\5901234123457.png",
      "text": "5901234123457"
    },
    {
      "value": "400638133393",
      "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-dH0Pbu\\barcodes-ean13\\4006381333931.png",
      "text": "4006381333931"
    }
  ],
  "errors": [
    {
      "line": 3,
      "value": "12345",
      "error": "EAN-13 needs 12 or 13 digits (you have 5)"
    }
  ]
}
qr_generateQR code generator

Make a QR code as SVG or PNG for a link, plain text, Wi-Fi login, contact card (vCard 3.0), email, SMS, phone call or map location. Same encoder as the website (qrcode.js: versions 1-40, error correction L/M/Q/H, smallest version that fits, best mask), UTF-8 so Arabic/emoji work. Options: size in px, quiet-zone margin, colours, square/rounded/dots modules, and an optional centre logo (forces level H). Returns the file path, the exact encoded text, version, module count and contrast warnings. Max about 2.9 KB of data at level L. Web version: https://s3eidat.github.io/qr-studio/

InputTypeDescription
type"url" | "text" | "wifi" | "vcard" | "email" | "sms" | "tel" | "geo"What the code holds. Default: "url" if url is given, else "text".
textstringtext: any text (encoded exactly as given).
urlstringurl: web address; "https://" is added when no scheme is given.
ssidstringwifi: network name.
passwordstringwifi: password (ignored for open networks).
security"WPA" | "WEP" | "nopass"wifi: WPA (WPA/WPA2/WPA3, default), WEP, or nopass (open).
hiddenbooleanwifi: the network is hidden.
firstNamestringvcard: first name.
lastNamestringvcard: last name.
phonestringvcard/sms/tel: phone number.
emailstringvcard: email address.
companystringvcard: company.
jobTitlestringvcard: job title.
websitestringvcard: website.
addressstringvcard: address.
tostringemail: recipient address.
subjectstringemail: subject.
bodystringemail: message body.
messagestringsms: message text.
latitudenumber | stringgeo: latitude, -90..90.
longitudenumber | stringgeo: longitude, -180..180.
labelstringgeo: optional place label (?q=).
ecc"L" | "M" | "Q" | "H"
default "M"
Error correction: L 7%, M 15% (default), Q 25%, H 30%. A logo forces H.
mode"byte" | "auto" | "numeric" | "alphanumeric"
default "byte"
Encoding mode. "byte" (UTF-8, what the website uses), "auto" (numeric/alphanumeric when the text allows, smaller codes), or force one.
versioninteger
default 0
QR version 1-40 (size), or 0 = smallest that fits (default).
format"svg" | "png"
default "png"
Output format.
sizeinteger
default 1024
Image width/height in px (default 1024).
margininteger
default 4
Quiet zone in modules (default 4; under 2 may not scan).
foregroundstring
default "#0d0f12"
Module colour, hex (default #0d0f12).
backgroundstring
default "#ffffff"
Background colour, hex (default #ffffff).
shape"square" | "rounded" | "dots"
default "square"
Module shape. Rounded/dots also round the three finder squares.
logostringOptional centre logo image (path or base64, max 5 MB). Covers ~22% of the code; error correction becomes H.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example: Encodes "https://example.com" as a 1024 px PNG.

{
  "url": "example.com"
}

Result

{
  "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-Y92THC\\qr-url.png",
  "bytes": 17533,
  "mimeType": "image/png",
  "payload": "https://example.com",
  "version": 2,
  "modules": 25,
  "ecc": "M",
  "mode": "Byte",
  "mask": 5,
  "dataBytes": 19,
  "contrast": 19.19,
  "warnings": []
}

Example: payload WIFI:T:WPA;S:Home;P:12345678;;

{
  "type": "wifi",
  "ssid": "Home",
  "password": "12345678",
  "format": "svg"
}

Result

{
  "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-SUmYUZ\\qr-wifi.svg",
  "bytes": 5788,
  "mimeType": "image/svg+xml",
  "payload": "WIFI:T:WPA;S:Home;P:12345678;;",
  "version": 3,
  "modules": 29,
  "ecc": "M",
  "mode": "Byte",
  "mask": 1,
  "dataBytes": 30,
  "contrast": 19.19,
  "warnings": []
}

Example

{
  "type": "vcard",
  "firstName": "Sara",
  "lastName": "Haddad",
  "phone": "+962 7 9000 0000",
  "shape": "rounded"
}

Result

{
  "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-nWOSYW\\qr-vcard.png",
  "bytes": 138314,
  "mimeType": "image/png",
  "payload": "BEGIN:VCARD\r\nVERSION:3.0\r\nN:Haddad;Sara;;;\r\nFN:Sara Haddad\r\nTEL;TYPE=CELL:+962 7 9000 0000\r\nEND:VCARD",
  "version": 6,
  "modules": 41,
  "ecc": "M",
  "mode": "Byte",
  "mask": 4,
  "dataBytes": 101,
  "contrast": 19.19,
  "warnings": []
}
qr_readQR code reader

Read (decode) a QR code from an image file: photo, screenshot or scan (PNG, JPEG, WebP, GIF, TIFF, AVIF, SVG). Uses jsQR like the website, trying several sizes and inverted (light-on-dark) codes. Returns the text plus what it is (Link, Wi-Fi, Contact, Email, SMS, Phone, Location, Event or Text) with parsed fields, and safety notes for links (http, hidden @ user part, punycode lookalikes, bare IPs, odd ports). Links are never opened. Reads one code per image. Web version (with camera): https://s3eidat.github.io/qr-scanner/

InputTypeDescription
image requiredstringThe image containing a QR code: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).

Example: e.g. { text: "WIFI:T:WPA;S:Home;P:secret;;", type: "Wi-Fi", fields: [{label:"Network",value:"Home"},…] }

{
  "image": "/path/to/photo-of-qr.jpg"
}
qr_wifiWi-Fi QR code and card

Make a Wi-Fi QR code that phones join by scanning (WPA/WPA2/WPA3, WEP or open; hidden networks), as PNG or SVG, or a printable PDF card like the website's: coloured header with the Wi-Fi symbol, "Scan to join", the QR code, network name and (optionally) the password, on an A6 card, an A4 poster, or A4 sheets of 4 or 9 cards to cut. Special characters (; , : " \) are escaped. The PDF card uses English labels and Latin text only for now. Web version: https://s3eidat.github.io/wifi-card/

InputTypeDescription
ssid requiredstringNetwork name (SSID).
passwordstring
default ""
Wi-Fi password (ignored for open networks).
security"WPA" | "WEP" | "nopass"
default "WPA"
WPA (WPA/WPA2/WPA3, default), WEP, or nopass (open network).
hiddenboolean
default false
The network is hidden (not broadcast).
format"png" | "svg" | "pdf"
default "pdf"
pdf = printable card sheet (default); png/svg = just the QR code.
layout"a6" | "a4" | "a4x4" | "a4x9"
default "a4x4"
PDF: a6 = one A6 card; a4 = one A4 poster; a4x4 = A4 sheet of 4 cards (default); a4x9 = A4 sheet of 9 small cards.
theme"teal" | "ink" | "coral" | "violet" | "saver"
default "teal"
PDF: card colour; "saver" is an ink-saving outline.
titlestring
default ""
PDF: heading on the card (default "Wi-Fi").
showPasswordboolean
default true
PDF: print the password under the code.
sizeinteger
default 1024
PNG/SVG: image size in px.
margininteger
default 4
PNG/SVG: quiet zone in modules.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example: A4 sheet with 4 cards; payload WIFI:T:WPA;S:S3eidat Café;P:coffee-is-free;;

{
  "ssid": "S3eidat Café",
  "password": "coffee-is-free"
}

Result

{
  "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-jx8uxA\\wifi-card.pdf",
  "bytes": 6360,
  "mimeType": "application/pdf",
  "pages": 1,
  "payload": "WIFI:T:WPA;S:S3eidat Café;P:coffee-is-free;;",
  "version": 4,
  "modules": 33,
  "warnings": []
}

Example: payload WIFI:T:nopass;S:Guest;;

{
  "ssid": "Guest",
  "security": "nopass",
  "format": "png"
}

Result

{
  "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-REoI5U\\wifi-qr.png",
  "bytes": 17598,
  "mimeType": "image/png",
  "payload": "WIFI:T:nopass;S:Guest;;",
  "version": 2,
  "modules": 25,
  "warnings": []
}

Document makers

Invoices, receipts, business cards, labels, calendars and CVs as PDF.

doc_attendance_sheetAttendance sheet PDF

Make a printable attendance / sign-in sheet PDF: names down the side, one column per date for a month (skipping chosen weekdays, e.g. Fri+Sat or Sat+Sun), a run of N sessions from a start date, or N blank columns; tick boxes or wide signature cells; row numbers, a total column, extra empty rows, title, group and teacher. Same page maths as the website: rows that don't fit continue on more pages, and too many date columns are split across pages with the same names. Also returns a blank register CSV. English labels, Latin text only for now. Web version: https://s3eidat.github.io/attendance-sheet/

InputTypeDescription
names requiredstring | string[]Names, one per line (leading "1." numbering and anything after a tab are removed), or a list.
titlestring
default ""
Title (default "Attendance").
groupstring
default ""
Class / group, shown under the title.
teacherstring
default ""
Teacher, shown under the title.
mode"month" | "sessions" | "blank"
default "month"
month = every day of a month; sessions = N dates from a start date; blank = N unnamed columns.
monthstringmonth mode: YYYY-MM (default this month).
startstringsessions mode: first date YYYY-MM-DD (default today).
countinteger
default 12
sessions/blank: number of columns.
skipinteger[]
default [0,6]
Weekdays to skip, 0 = Sunday … 6 = Saturday (default [0, 6] = weekends Sat+Sun; use [5, 6] for Fri+Sat).
cell"box" | "sign"
default "box"
box = tick boxes, sign = wide cells for signatures.
paper"A4" | "Letter"
default "A4"
orientation"landscape" | "portrait"
default "landscape"
rowHeightnumberRow height in mm (default 8, or 13 for signature cells).
extraRowsinteger
default 2
Empty rows after the names.
numbersboolean
default true
Row number column.
totalboolean
default true
Total column.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example: 22 date columns, 1 page.

{
  "names": "Ahmad Khalil\nLina Haddad\nOmar Saleh",
  "group": "Grade 7 B · Science",
  "teacher": "Ms. Rana",
  "month": "2026-11",
  "skip": [
    5,
    6
  ]
}

Result

{
  "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-lzAwwv\\attendance.pdf",
  "bytes": 11902,
  "mimeType": "application/pdf",
  "pages": 1,
  "names": 3,
  "columns": 22,
  "rowsPerPage": 19,
  "columnChunks": 1,
  "dates": [
    "2026-11-01",
    "2026-11-02",
    "2026-11-03",
    "2026-11-04",
    "2026-11-05",
    "2026-11-08",
    "2026-11-09",
    "2026-11-10",
    "2026-11-11",
    "2026-11-12",
    "2026-11-15",
    "2026-11-16",
    "2026-11-17",
    "2026-11-18",
    "2026-11-19",
    "2026-11-22",
    "2026-11-23",
    "2026-11-24",
    "2026-11-25",
    "2026-11-26",
    "2026-11-29",
    "2026-11-30"
  ],
  "csv": "Name,2026-11-01,2026-11-02,2026-11-03,2026-11-04,2026-11-05,2026-11-08,2026-11-09,2026-11-10,2026-11-11,2026-11-12,2026-11-15,2026-11-16,2026-11-17,2026-11-18,2026-11-19,2026-11-22,2026-11-23,2026-11-24,2026-11-25,2026-11-26,2026-11-29,2026-11-30\r\nAhmad Khalil,,,,,,,,,,,,,,,,,,,,,,\r\nLina Haddad,,,,,,,,,,,,,,,,,,,,,,\r\nOmar Saleh,,,,,,,,,,,,,,,,,,,,,,\r\n"
}
doc_business_cardsBusiness cards PDF

Design business cards and get a print-ready PDF sheet: cards centred on A4 or Letter with crop marks (10 per sheet for 85 × 55 mm EU or 3.5 × 2 in US cards), plus a back side page mirrored for long-edge duplex printing. Four front templates (classic, band, centered, minimal), backs with your logo, a "scan to save my contact" vCard QR code, or blank; six colour themes or your own accent/paper/ink colours; optional logo and a vCard QR on the front. Also returns the vCard text. Print at 100% / actual size. Latin text only for now (the website also does an Arabic side). Web version: https://s3eidat.github.io/business-card/

InputTypeDescription
name requiredstringYour name.
titlestring
default ""
Job title.
companystring
default ""
Company.
phonestring
default ""
emailstring
default ""
websitestring
default ""
addressstring
default ""
taglinestring
default ""
Short line for the back.
template"classic" | "band" | "centered" | "minimal"
default "classic"
Front layout.
back"logo" | "qr" | "none"
default "logo"
Back side: logo/company on the accent colour, a contact QR code, or none.
qrFrontboolean
default false
Also put the contact QR code on the front.
theme"teal" | "navy" | "charcoal" | "forest" | "violet" | "mono"
default "teal"
Colour preset (navy = navy & gold).
accentstringOverride the accent colour.
paperColorstringOverride the card background colour.
inkstringOverride the text colour.
size"eu" | "us"
default "eu"
eu = 85 × 55 mm, us = 3.5 × 2 in.
sheet"A4" | "Letter"
default "A4"
Paper for the print sheet.
gapnumber
default 0
Gap between cards in mm (0 = shared cut lines).
sides"both" | "front" | "back"
default "both"
Which pages to include (both = front page then mirrored back page).
logostringOptional logo image: a PNG/JPEG/WebP/GIF/SVG file path or base64. Drawn as-is (keep it small).
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example: A4 sheet of 10 fronts + mirrored sheet of 10 QR backs.

{
  "name": "Lina Haddad",
  "title": "Optometrist",
  "company": "Lumen Eye Clinic",
  "phone": "+962 79 000 0000",
  "email": "lina@lumen.example",
  "website": "lumen.example",
  "address": "Amman, Jordan",
  "back": "qr"
}

Result

{
  "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-eTOUAz\\lina-haddad-cards.pdf",
  "bytes": 18458,
  "mimeType": "application/pdf",
  "pages": 2,
  "cardsPerSheet": 10,
  "columns": 2,
  "rows": 5,
  "vcard": "BEGIN:VCARD\r\nVERSION:3.0\r\nN:Haddad;Lina;;;\r\nFN:Lina Haddad\r\nORG:Lumen Eye Clinic\r\nTITLE:Optometrist\r\nTEL;TYPE=CELL:+962 79 000 0000\r\nEMAIL;TYPE=INTERNET:lina@lumen.example\r\nURL:lumen.example\r\nADR;TYPE=WORK:;;Amman\\, Jordan;;;;\r\nEND:VCARD",
  "qrVersion": 11
}
doc_calendarCalendar PDF

Make a printable calendar PDF: a month grid (one page per month, several months in a row if you like) or a whole year on one page, A4 or Letter, landscape or portrait. Week starts on Saturday (default, like the website), Sunday or Monday; optional Hijri (Umm al-Qura) dates; events (one-off or yearly, 7 colours) shown in the day cells, or as coloured days plus a legend in the year view; optional title. English names by default; other Latin-script locales (fr, de, es, tr…) use their own month/day names. Web version: https://s3eidat.github.io/calendar-maker/

InputTypeDescription
view"month" | "year"
default "month"
month = month grid pages, year = 12 months on one page.
yearintegerYear (default: this year).
monthintegerMonth 1-12 (month view; default: this month).
monthsinteger
default 1
Month view: how many consecutive months (one page each).
weekStart0 | 1 | 6
default 6
First day of the week: 6 = Saturday (default), 0 = Sunday, 1 = Monday.
hijriboolean
default false
Show Hijri (Umm al-Qura) dates.
paper"A4" | "Letter"
default "A4"
orientation"landscape" | "portrait"
default "landscape"
titlestring
default ""
Optional title above the month name, e.g. "Family calendar".
localestring
default "en"
Language for month/day names (default en). Latin-script locales only.
eventsobject[]
default []
Events to show.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example

{
  "year": 2026,
  "month": 12,
  "title": "Family calendar",
  "hijri": true,
  "events": [
    {
      "date": "2026-12-03",
      "text": "Sara's birthday",
      "color": "purple",
      "yearly": true
    }
  ]
}

Result

{
  "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-MDrckN\\calendar-2026-12.pdf",
  "bytes": 4028,
  "mimeType": "application/pdf",
  "pages": 1,
  "from": "2026-12",
  "to": "2026-12"
}

Example

{
  "view": "year",
  "year": 2027,
  "weekStart": 1,
  "orientation": "portrait"
}

Result

{
  "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-i8ED9t\\calendar-2027.pdf",
  "bytes": 11458,
  "mimeType": "application/pdf",
  "pages": 1,
  "from": "2027-01",
  "to": "2027-12"
}
doc_invoiceInvoice PDF

Make a clean A4 invoice PDF: your business (logo, name, address, email, phone, tax ID), client, invoice number and dates, line items with quantity, unit price, per-line discount % and tax %, subtotal, discount, tax per rate and total, notes, payment terms, and an optional PAID stamp. Same maths as the website: every line is rounded to the currency's minor unit (2 decimals, 3 for JOD/KWD/BHD/OMR/TND…, 0 for JPY…) with half-up rounding; amounts formatted en-US style ($1,234.50, JOD 10.124). Long item lists continue on more pages. Returns the PDF path and the computed totals. Latin text only for now. Web version: https://s3eidat.github.io/invoice-maker/

InputTypeDescription
numberstring
default "INV-0001"
Invoice number.
datestringIssue date, YYYY-MM-DD (default today).
duestringDue date, YYYY-MM-DD (optional).
currency"USD" | "EUR" | "GBP" | "JOD" | "SAR" | "AED" | "EGP" | "KWD" | "QAR" | "BHD" | "OMR" | "IQD" | "LBP" | "MAD" | "TND" | "DZD" | "TRY" | "INR" | "PKR" | "CAD" | "AUD" | "NZD" | "JPY" | "CNY" | "CHF" | "SEK" | "NOK" | "DKK" | "PLN" | "ZAR" | "BRL" | "MXN" | "SGD"
default "USD"
Currency code.
accentstring
default "#0e9f9a"
Accent colour (hex).
paidboolean
default false
Mark as paid (shows "Amount paid" and a PAID stamp).
fromobject
default {"name":"","email":"","phone":"","address":"","taxId":""}
Your business: name, email, phone, address, taxId.
toobject
default {"name":"","email":"","address":""}
The client (bill to): name, email, address.
items requiredobject[]Line items: desc, qty, price, disc %, tax %.
notesstring
default ""
Notes shown at the bottom.
termsstring
default ""
Payment terms shown at the bottom.
logostringOptional logo image: a PNG/JPEG/WebP/GIF/SVG file path or base64. Drawn as-is (keep it small).
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example: Subtotal $1,540.00, discount $34.00, tax 8% $120.48, total $1,626.48.

{
  "number": "INV-0042",
  "currency": "USD",
  "from": {
    "name": "Northwind Studio",
    "email": "hello@northwind.studio"
  },
  "to": {
    "name": "Blue Fern Café"
  },
  "items": [
    {
      "desc": "Brand identity design",
      "qty": 1,
      "price": 1200,
      "tax": 8
    },
    {
      "desc": "Menu layout (per page)",
      "qty": 4,
      "price": 85,
      "disc": 10,
      "tax": 8
    }
  ],
  "terms": "Bank transfer within 14 days."
}

Result

{
  "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-62gT1b\\invoice-INV-0042.pdf",
  "bytes": 3109,
  "mimeType": "application/pdf",
  "pages": 1,
  "subtotal": 1540,
  "discount": 34,
  "tax": 120.48,
  "total": 1626.48,
  "taxByRate": {
    "8": 120.48
  },
  "lineAmounts": [
    1200,
    306
  ],
  "currency": "USD",
  "decimals": 2,
  "formattedTotal": "$1,626.48"
}
doc_labelsLabel sheets PDF

Print address or product labels on standard sticker sheets as PDF: A4 21/24/14/40/65-up (L7160, L7159, L7163, L7654, L7651 sizes) or Letter 30/10/80-up (5160, 5163, 5167 sizes), or custom geometry in mm. Text modes: "list" (one label per block, blank line between labels), "csv" (rows filled into a template like "{Name}\n{Street}\n{City} {Postcode}", header row or {1},{2}…), or "same" (one text × count). Copies per label, start at a later position to reuse a part-used sheet, alignment, font, size, bold first line, border, shrink-to-fit and printer offset nudges. Print at 100% / actual size. Up to 3000 labels. Latin text only for now. Web version: https://s3eidat.github.io/label-maker/

InputTypeDescription
mode"list" | "csv" | "same"
default "list"
list = blocks separated by a blank line; csv = rows + template; same = one text repeated.
textstringThe labels text (list blocks, CSV/TSV, or the single text).
pathstringOr: a text/CSV file path to read it from.
templatestring
default ""
csv: template with {Column name} or {1}-style placeholders (empty = all columns, one per line).
headerboolean
default true
csv: the first row holds column names.
countinteger
default 21
same: how many labels.
copiesinteger
default 1
list/csv: copies of each label.
preset"a4-21" | "a4-24" | "a4-14" | "a4-40" | "a4-65" | "lt-30" | "lt-10" | "lt-80" | "custom"
default "a4-21"
Sheet: a4-21 (63.5×38.1), a4-24 (63.5×33.9), a4-14 (99.1×38.1), a4-40 (45.7×25.4), a4-65 (38.1×21.2), lt-30 (2⅝×1 in), lt-10 (4×2 in), lt-80 (1¾×½ in), or custom.
customobjectpreset "custom": paper, cols, rows, w, h, top, left, gx, gy (mm).
startAtinteger
default 1
Start at this label position on the first sheet (skips used labels).
fontSizenumber
default 11
Font size in pt (default 11).
font"sans" | "serif" | "mono"
default "sans"
align"left" | "center" | "right"
default "center"
valign"top" | "middle"
default "middle"
boldboolean
default true
Bold first line (e.g. the name).
fitboolean
default true
Shrink text (in 0.5 pt steps, min 5 pt) until it fits the label.
borderboolean
default false
Draw a thin rounded border around each label.
paddingnumber
default 3
Inner padding in mm.
offsetXnumber
default 0
Printer nudge right (mm, negative = left).
offsetYnumber
default 0
Printer nudge down (mm, negative = up).
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example: 2 address labels on an A4 21-up sheet.

{
  "text": "Lina Haddad\n12 Rainbow Street\nAmman 11181\n\nOmar Saleh\n4 Garden Lane\nIrbid 21110",
  "border": true
}

Result

{
  "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-ngqEIy\\labels.pdf",
  "bytes": 1545,
  "mimeType": "application/pdf",
  "pages": 1,
  "labels": 2,
  "perSheet": 21,
  "shrunk": 0,
  "truncated": 0
}

Example

{
  "mode": "csv",
  "text": "Name,City\nLina,Amman\nOmar,Irbid",
  "template": "{Name}\n{City}",
  "preset": "lt-30"
}

Result

{
  "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-VE8GV4\\labels.pdf",
  "bytes": 1091,
  "mimeType": "application/pdf",
  "pages": 1,
  "labels": 2,
  "perSheet": 30,
  "shrunk": 0,
  "truncated": 0
}

Example

{
  "mode": "same",
  "text": "Homemade Fig Jam\n250 g",
  "count": 24,
  "preset": "a4-24"
}

Result

{
  "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-cNF3mK\\labels.pdf",
  "bytes": 2089,
  "mimeType": "application/pdf",
  "pages": 1,
  "labels": 24,
  "perSheet": 24,
  "shrunk": 0,
  "truncated": 0
}
doc_receiptReceipt PDF

Make a simple sales receipt as PDF: an 80 mm thermal-roll receipt (page height fits the content, monospace) or an A5 page. Shop header (name, address, phone, tax number), receipt number with prefix, date/time, customer, items (name × qty × price), discount (% or fixed amount), tax % after discount, total, payment method, amount paid and change, note and footer. Same maths as the website: amounts rounded half-up to 0, 2 or 3 decimals. Also returns the totals and a 32-column plain-text version. English labels, Latin text only for now. Web version: https://s3eidat.github.io/receipt-maker/

InputTypeDescription
shopobject
default {"name":"","address":"","phone":"","taxId":"","currency":"JD","decimals":2,"prefix":"R-","footer":"Thank you!"}
Shop details: name, address, phone, taxId, currency, decimals, prefix, footer.
nointeger
default 1
Receipt number.
whenstringDate and time, YYYY-MM-DDTHH:MM (default now).
customerstring
default ""
Customer name or table (optional).
pay"cash" | "card" | "transfer" | "wallet" | "other"
default "cash"
Payment method.
items requiredobject[]Items: name, qty, price.
discnumber
default 0
Discount: a percentage, or an amount when discType is "amt".
discType"pct" | "amt"
default "pct"
pct = percent of subtotal (default), amt = fixed amount.
taxnumber
default 0
Tax rate % applied after the discount.
paidnumber
default 0
Amount the customer paid (shows change).
notestring
default ""
Note line above the footer.
format"thermal" | "a5"
default "thermal"
thermal = 80 mm roll (default), a5 = A5 page.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example: Subtotal 8.000, discount 0.800, tax 1.152, total 8.352 JD, change 1.648.

{
  "shop": {
    "name": "Zaytoun Corner Café",
    "address": "Rainbow St. 12, Amman",
    "currency": "JD",
    "decimals": 3
  },
  "no": 1043,
  "customer": "Table 4",
  "paid": 10,
  "disc": 10,
  "tax": 16,
  "items": [
    {
      "name": "Cappuccino",
      "qty": 2,
      "price": 2.25
    },
    {
      "name": "Za'atar manakeesh",
      "qty": 1,
      "price": 1.5
    },
    {
      "name": "Fresh orange juice",
      "qty": 1,
      "price": 2
    }
  ]
}

Result

{
  "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-y912kN\\receipt-R-1043.pdf",
  "bytes": 2399,
  "mimeType": "application/pdf",
  "pages": 1,
  "subtotal": 8,
  "discount": 0.8,
  "tax": 1.152,
  "total": 8.352,
  "paid": 10,
  "change": 1.648,
  "text": "      Zaytoun Corner Café\n     Rainbow St. 12, Amman\n--------------------------------\n            Receipt\nNo.                       R-1043\nDate           15/01/2026  12:30\nCustomer                 Table 4\n--------------------------------\nCappuccino\n  2 × 2.250 JD          4.500 JD\nZa'atar manakeesh\n  1 × 1.500 JD          1.500 JD\nFresh orange juice\n  1 × 2.000 JD          2.000 JD\n--------------------------------\nSubtotal                8.000 JD\nDiscount 10%           −0.800 JD\nTax 16%                 1.152 JD\nTOTAL                   8.352 JD\nPayment                     Cash\nPaid             …"
}
doc_resumeResume / CV PDF

Turn structured CV fields into a clean A4 resume PDF: contact header, summary, experience, education, skills, projects and languages, in any section order, with three templates like the website: "classic" (centred header and accent rule), "modern" (coloured sidebar with contact, skills and languages) and "compact" (denser, headings in a left column). Description lines starting with "-", "•", "*" or "–" become bullet points; skills are comma-separated. Aims for one page; longer CVs continue on a second page. English headings, Latin text only for now. Web version: https://s3eidat.github.io/resume-builder/

InputTypeDescription
template"classic" | "modern" | "compact"
default "classic"
accentstring
default "#0e7c86"
Accent colour (hex).
order("summary" | "experience" | "education" | "skills" | "projects" | "languages")[]Section order; missing sections are appended in the default order.
contactobject
default {"name":"","title":"","email":"","phone":"","location":"","website":""}
summarystring
default ""
Two or three lines about you.
experienceobject[]
default []
educationobject[]
default []
skillsstring | string[]
default ""
Skills: comma-separated text or a list.
projectsobject[]
default []
languagesobject[]
default []
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example

{
  "template": "modern",
  "contact": {
    "name": "Layla Haddad",
    "title": "Senior Product Designer",
    "email": "layla@example.com",
    "location": "Amman, Jordan"
  },
  "summary": "Product designer with 8 years of experience.",
  "experience": [
    {
      "role": "Senior Product Designer",
      "org": "Mosaic Health",
      "start": "2022",
      "end": "Present",
      "desc": "- Redesigned patient booking; completion up 31%\n- Built a design system"
    }
  ],
  "skills": "Figma, Prototyping, User research",
  "languages": [
    {
      "name": "Arabic",
      "level": "Native"
    },
    {
      "name": "English",
      "level": "Fluent"
    }
  ]
}

Result

{
  "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-6FSQmm\\Layla-Haddad.pdf",
  "bytes": 2360,
  "mimeType": "application/pdf",
  "pages": 1
}

For AI agents

Extra tools made for agents: documents to Markdown, OCR, chunking, SQL over CSV, charts and more.

ai_chartChart to SVG/PNG

Render a chart to an SVG or PNG file, offline. Either give a simple description ({ type: bar|line|area|pie|scatter, data: rows, x, y, color?, title? }; y may be a list of fields for multi-series line/bar charts), or a full Vega-Lite (or Vega) spec in "spec" with the rows inline in data.values. Field types are inferred (numbers = quantitative, ISO dates = temporal, other = categories). Loading data or images from URLs or files is refused. Up to 50,000 rows. PNG is rasterised from the SVG at "scale" (default 2×).

InputTypeDescription
specobjectA full Vega-Lite spec (or Vega, if its $schema says so). Data must be inline (data.values). Use this or "type".
type"bar" | "line" | "area" | "pie" | "scatter"Simple chart type. Use with data, x and y.
dataobject[]Rows for the simple chart, e.g. [{"month":"Jan","sales":12}].
xstringField for the x axis (the category / slice label for pie).
ystring | string[]Numeric field for the y axis (slice size for pie). A list of fields draws one series per field.
colorstringOptional field that splits the data into coloured series.
titlestring
widthintegerPlot width in px (default 600; pie 360).
heightintegerPlot height in px (default 360).
format"svg" | "png"
default "svg"
Output format: svg (default) or png.
scalenumber
default 2
PNG only: pixel density multiplier (2 = twice the SVG size, sharp on high-DPI screens). Default 2.
backgroundstring
default "#ffffff"
Background colour. Default white; "transparent" for none.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example

{
  "type": "bar",
  "data": [
    {
      "month": "Jan",
      "sales": 12
    },
    {
      "month": "Feb",
      "sales": 19
    },
    {
      "month": "Mar",
      "sales": 7
    }
  ],
  "x": "month",
  "y": "sales",
  "title": "Sales"
}

Result

{
  "format": "svg"
}

Example

{
  "type": "pie",
  "data": [
    {
      "k": "A",
      "v": 3
    },
    {
      "k": "B",
      "v": 1
    }
  ],
  "x": "k",
  "y": "v",
  "format": "png"
}

Result

{
  "format": "png",
  "mimeType": "image/png"
}
ai_chunk_textChunk text for RAG

Split text, markdown or a text file into token-sized chunks for retrieval (RAG) and embeddings. Splits at markdown headings (ATX "#" headings, never inside code fences) and paragraphs first, then sentences (lines inside code blocks), and only cuts mid-sentence when a single sentence is longer than maxTokens. Consecutive chunks of the same section share about overlapTokens of text. Each chunk returns index, text, tokens, startChar/endChar (JavaScript string offsets, end exclusive, so text === source.slice(startChar, endChar) once CRLF line endings are normalised to \n) and the heading path it sits under. Tokens are counted with tiktoken (o200k_base by default; for Claude treat counts as estimates). Give "text" or "path" (file path or base64); give "output" to also write the chunks as JSONL (one JSON object per line).

InputTypeDescription
textstringThe text (inline).
pathstringOr: a UTF-8 text/markdown file, as a local path or base64 (raw or a data: URI).
maxTokensinteger
default 500
Largest chunk, in tokens. Default 500.
overlapTokensinteger
default 50
Tokens repeated from the end of the previous chunk (same section only). Default 50. Must be below maxTokens.
markdownboolean
default true
Treat "#" lines as section headings (default true). Set false for plain text.
encoding"o200k_base" | "cl100k_base"
default "o200k_base"
Tokenizer: "o200k_base" (GPT-4o / o-series, default) or "cl100k_base" (GPT-4 / GPT-3.5 / text-embedding-3).
outputstringAlso write the chunks as JSONL to this file (or folder ending in "/").
includeTextbooleanReturn each chunk's text inline. Default: true, or false when "output" is given.
maxChunksinteger
default 2000
Most chunks to return inline (the JSONL file always has all). Default 2000.

Example: Two chunks, headings ["Guide","Install"] and ["Guide","Use"].

{
  "text": "# Guide\n\n## Install\n\nRun npm install.\n\n## Use\n\nCall the tool.",
  "maxTokens": 100,
  "overlapTokens": 0
}

Result

{
  "chunkCount": 2,
  "totalTokens": 17,
  "encoding": "o200k_base",
  "maxTokens": 100,
  "overlapTokens": 0,
  "chunks": [
    {
      "index": 0,
      "tokens": 10,
      "startChar": 0,
      "endChar": 37,
      "headings": [
        "Guide",
        "Install"
      ],
      "text": "# Guide\n\n## Install\n\nRun npm install."
    },
    {
      "index": 1,
      "tokens": 7,
      "startChar": 39,
      "endChar": 61,
      "headings": [
        "Guide",
        "Use"
      ],
      "text": "## Use\n\nCall the tool."
    }
  ]
}

Example

{
  "path": "handbook.md",
  "maxTokens": 400,
  "overlapTokens": 40,
  "output": "chunks.jsonl"
}
ai_create_pptxCreate a PowerPoint deck

Build a 16:9 PowerPoint (.pptx) deck from a simple outline: an optional title slide, then slides with a title plus any of bullets (nested), a paragraph, an image (file path or base64), a table (first row = header; long tables continue on extra slides), a native editable chart (bar, line, pie) and speaker notes. Light or dark theme with clean, readable defaults; text plus a visual are laid out side by side. Arabic/Hebrew text is set right-to-left. Up to 300 slides. Opens in PowerPoint, Keynote, Google Slides and LibreOffice.

InputTypeDescription
titlestringDeck title: adds a title slide and sets the file title.
subtitlestringSubtitle on the title slide.
authorstring
theme"light" | "dark"
default "light"
slides requiredobject[]
slideNumbersboolean
default true
Show slide numbers on content slides. Default true.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example

{
  "title": "Q3 Review",
  "subtitle": "Sales team",
  "theme": "light",
  "slides": [
    {
      "title": "Highlights",
      "bullets": [
        "Revenue up 12%",
        [
          "EMEA +20%",
          "APAC +8%"
        ],
        "Two new clients"
      ],
      "notes": "Start with the good news."
    },
    {
      "title": "Revenue by month",
      "chart": {
        "type": "bar",
        "labels": [
          "Jul",
          "Aug",
          "Sep"
        ],
        "series": [
          {
            "name": "Revenue",
            "values": [
              120,
              135,
              150
            ]
          }
        ]
      }
    },
    {
      "title": "Team",
      "table": [
        [
          "Name",
          "Deals"
        ],
        [
          "Sara",
          12
        ],
        [
          "Omar",
          9
        ]
      ]
    }
  ]
}

Result

{
  "slides": 4
}
ai_create_xlsxCreate an Excel workbook (.xlsx)

Build a real Excel .xlsx file from data: one or more sheets, each with optional column definitions (header, key, width, numFmt) and rows given as objects or arrays. Bold shaded header, frozen header row and filter buttons (both on by default), column widths fitted to the content, Excel number formats (raw codes like "#,##0.00" or aliases: integer, number, percent, currency, usd, eur, gbp, date, datetime, time, text), strings starting with "=" written as live formulas (=SUM(B2:B9), =B2*C2, cross-sheet refs), ISO date strings written as real dates, and an optional totals row (SUM/AVERAGE/COUNT/MIN/MAX formulas). Up to 50 sheets and 1,000,000 rows per sheet. Formulas are calculated by Excel when the file is opened. Writes to "output" (default "workbook.xlsx" in the output folder) and returns the path and a summary of each sheet.

InputTypeDescription
sheets requiredobject[]
parseDatesboolean
default true
Write ISO date strings as Excel dates (default true).
titlestringWorkbook title (file properties, and the default file name).
authorstringAuthor in the file properties.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example

{
  "sheets": [
    {
      "name": "Orders",
      "columns": [
        {
          "header": "Item"
        },
        {
          "header": "Qty",
          "numFmt": "integer"
        },
        {
          "header": "Price",
          "numFmt": "usd"
        },
        {
          "header": "Total",
          "numFmt": "usd"
        }
      ],
      "rows": [
        [
          "Pen",
          3,
          1.5,
          "=B2*C2"
        ],
        [
          "Book",
          1,
          12,
          "=B3*C3"
        ]
      ],
      "totalsRow": {
        "Qty": "sum",
        "Total": "sum"
      }
    }
  ]
}

Result

{
  "sheets": [
    {
      "name": "Orders",
      "rows": 2,
      "columns": [
        "Item",
        "Qty",
        "Price",
        "Total"
      ],
      "range": "A1:D4",
      "formulas": 4
    }
  ]
}
ai_csv_profileProfile a table (CSV, JSON, XLSX)

Profile a table to understand and clean it before analysis: row and column counts, and per column the inferred type (integer, number, boolean, date, datetime, string), non-null and null counts, unique count, top 5 values, sample values, min/max/mean/median/stddev for numbers, min/max for ISO dates, min/max length for text, plus data-quality warnings (mixed types, leading/trailing spaces, mostly missing, constant columns, duplicate rows, ragged headers). Input: "file" (local path or base64 of CSV, TSV, JSON array, JSONL or .xlsx, first sheet or "sheet") or inline "csv" / "json". Empty cells, "null" and "NULL" count as missing. Runs locally; nothing is uploaded.

InputTypeDescription
filestringA table file: local path (absolute, or relative to --root) or base64. CSV, TSV, JSON array, JSONL or .xlsx.
csvstringOr: CSV/TSV text inline (delimiter detected; first row is the header).
jsonstring | object[]Or: JSON inline: an array of objects (or of arrays, first one the header), as a value or as JSON/JSONL text.
sheetstringFor .xlsx: the sheet name to read (default: the first sheet).
format"auto" | "csv" | "tsv" | "json" | "jsonl" | "xlsx"
default "auto"
Force the file format (default "auto": by extension, then by content).
columnsstring[]Only profile these columns (default: all).

Example

{
  "csv": "name,age\nAda,36\nBob ,41\nAda,36\n,"
}

Result

{
  "rows": 4,
  "columns": 2,
  "duplicateRows": 1
}
ai_detect_languageDetect language

Detect the language of a text with franc-min (82 languages with 8M+ speakers, ISO 639-3 codes) and the writing systems it uses (Latin, Arabic, Cyrillic, Han/CJK, Hangul, Devanagari, Greek, Hebrew, Thai…). Returns the top 3 candidates with scores (1 = best match, relative to the others), English names and ISO 639-1 codes where they exist, plus a share per script. Texts under 20 characters are "too short": only the script is reported. Works best on a sentence or more of one language; for mixed text the dominant script wins; long texts are judged on their first 100,000 characters. Hebrew is not among the languages (script only). Give "text" or "path" (file path or base64).

InputTypeDescription
textstringThe text (inline).
pathstringOr: a UTF-8 text/markdown file, as a local path or base64 (raw or a data: URI).
onlystring[]Restrict to these ISO 639-3 codes (e.g. ["arb","eng","fra"]).

Example

{
  "text": "The quick brown fox jumps over the lazy dog near the river bank."
}

Result

{
  "language": "eng",
  "iso6391": "en",
  "script": "Latin"
}

Example

{
  "text": "مرحبا بكم في موقعنا، نتمنى لكم يوما سعيدا"
}

Result

{
  "language": "arb",
  "iso6391": "ar",
  "script": "Arabic"
}

Example

{
  "text": "Hi there"
}

Result

{
  "tooShort": true,
  "script": "Latin"
}
ai_diagramDiagram to SVG/PNG

Draw a flowchart / graph diagram from a tiny text language and save it as SVG or PNG, laid out automatically (dagre). One statement per line: `direction: LR` (TB, LR, BT, RL), `title: My flow`, `A [shape=diamond, label="Ready?", color=#fde68a]` (shapes: box, round, diamond, circle, ellipse), edges `A -> B: label`, chains `A -> B -> C`, `-->` dashed, `==>` thick, `<->` both ends, `--` no arrow; quote names with spaces ("Start here" -> A); `\n` in labels breaks lines; # starts a comment. Or pass JSON { nodes: [{id, label, shape, color}], edges: [{from, to, label, style, arrow}] } as "graph". Up to 500 nodes and 2000 edges. Labels use Helvetica/Arial metrics (Latin text is measured best).

InputTypeDescription
sourcestringDiagram text in the DSL (or a JSON graph as text).
graphobjectThe diagram as JSON instead of "source".
direction"TB" | "LR" | "BT" | "RL"Override the layout direction: TB (top-bottom, default), LR, BT, RL.
theme"light" | "dark"
default "light"
format"svg" | "png"
default "svg"
Output format: svg (default) or png.
scalenumber
default 2
PNG only: pixel density multiplier (2 = twice the SVG size, sharp on high-DPI screens). Default 2.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example

{
  "source": "direction: LR\nStart [shape=circle]\nCheck [shape=diamond, label=\"Valid?\"]\nStart -> Check\nCheck -> Done: yes\nCheck -> Fix: no\nFix --> Check"
}

Result

{
  "format": "svg",
  "nodes": 4,
  "edges": 4
}

Example

{
  "graph": {
    "nodes": [
      {
        "id": "a",
        "label": "Client"
      },
      {
        "id": "b",
        "label": "Server",
        "shape": "box"
      }
    ],
    "edges": [
      {
        "from": "a",
        "to": "b",
        "label": "HTTPS",
        "arrow": "both"
      }
    ]
  },
  "format": "png"
}

Result

{
  "format": "png",
  "nodes": 2,
  "edges": 1
}
ai_docx_to_markdownWord (.docx) to Markdown

Convert a Word .docx document to Markdown: headings (from Heading styles), bold/italic, lists, links, tables, footnotes. Images become placeholders like ![](image-1.png) unless embedImages is true (then data: URIs, which can be very large). Returns the number of conversion warnings (unrecognised styles and the like) and the first few messages. Old binary .doc files are not supported. Accepts a local path or base64. Inline Markdown is cut at maxChars (default 200k); "output" saves it to a file. Runs locally; nothing is uploaded.

InputTypeDescription
file requiredstringThe .docx document: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
embedImagesboolean
default false
Embed images as base64 data: URIs instead of placeholders. Default false.
maxCharsinteger
default 200000
Cut the inline Markdown after this many characters, with a note (a file written with "output" is never cut). Default 200000.
outputstringWrite the Markdown to this file (or folder ending in "/") and return its path instead of the inline text.

Example

{
  "file": "report.docx"
}

Example

{
  "file": "thesis.docx",
  "output": "thesis.md"
}
ai_extract_entitiesExtract entities

Pull structured items out of text or a text file, deduplicated with counts: emails, URLs, phone numbers (libphonenumber, validated, E.164 plus type/country; local numbers read with defaultRegion, default JO), dates (ISO, D/M/Y or M/D/Y, Y/M/D, English month names; normalised to YYYY-MM-DD; a date like 03/04/2024 is flagged ambiguous with the other reading), money ($, €, £, JOD/JD/د.أ, SAR/ر.س, AED/د.إ, KWD, EGP… and Arabic currency words, with amount and ISO currency), percentages, #hashtags, @mentions, IPv4/IPv6, IBANs (country length and mod-97 checked) and card-like numbers (only Luhn-valid ones with a known brand prefix, always masked: only the last 4 digits are shown, the full number is never returned). Arabic-Indic digits are understood. Regex-based, not NLP: no names, places or organisations. Give "text" or "path" (file path or base64).

InputTypeDescription
textstringThe text (inline).
pathstringOr: a UTF-8 text/markdown file, as a local path or base64 (raw or a data: URI).
types("emails" | "urls" | "phones" | "dates" | "money" | "percentages" | "hashtags" | "mentions" | "ipv4" | "ipv6" | "ibans" | "cards")[]Only these types (default all): emails, urls, phones, dates, money, percentages, hashtags, mentions, ipv4, ipv6, ibans, cards.
defaultRegionstring
default "JO"
Two-letter country for local phone numbers, regional currencies (dinar, riyal…) and D/M/Y vs M/D/Y. Default JO.
maxPerTypeinteger
default 500
Most unique items to return per type. Default 500.

Example

{
  "text": "Mail omar@example.com or call 079 123 4567 before 15/03/2025. Fee: 25 JOD (10% off). #sale @s3eidat"
}

Result

{
  "counts": {
    "emails": 1,
    "phones": 1,
    "dates": 1,
    "money": 1,
    "percentages": 1,
    "hashtags": 1,
    "mentions": 1
  }
}
ai_file_identifyIdentify a file type

Find out what a file really is from its content (magic bytes, via file-type: images, video, audio, archives, Office, PDF, fonts, executables… ~150 formats), not its name. Reports the detected type and MIME, whether the extension disagrees with the content, size, SHA-256, and for text files an encoding guess (UTF-8, UTF-8/16/32 with BOM, UTF-16 without BOM, ASCII, 8-bit legacy) plus a text kind (JSON, XML, SVG, HTML…). Accepts a file path or base64. Nothing is executed.

InputTypeDescription
file requiredstringThe file to identify: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
namestringFile name to compare the extension against (handy for base64 input). Default: the path's own name.

Example

{
  "file": "base64:iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
  "name": "photo.jpg"
}

Result

{
  "detected": {
    "ext": "png",
    "mime": "image/png"
  },
  "mismatch": true
}
ai_html_to_markdownHTML to Markdown

Convert HTML to clean Markdown for reading or feeding to a model: headings, lists, links, images, code blocks, GitHub-style tables and strikethrough. Give the HTML inline ("html") or as a file ("path": local path or base64). Optional "readable" keeps only the main article (Mozilla Readability, falls back to the whole body), "selector" converts only matching elements, "baseUrl" makes relative links absolute, "includeLinks": false drops link URLs and images. Scripts, styles and forms are removed. Inline Markdown is cut at maxChars (default 200k) with a note; give "output" to save the whole thing to a .md file. Runs locally; nothing is uploaded.

InputTypeDescription
htmlstringThe HTML text. Give this or "path".
pathstringAn .html file instead of "html": local path or base64.
readableboolean
default false
Keep only the main content (article text), dropping navigation, ads and footers.
selectorstringCSS selector: convert only the matching elements (e.g. "main", "article .post").
baseUrlstringURL the HTML came from, to make relative links and images absolute.
includeLinksboolean
default true
Keep links and images. False turns links into plain text and drops images.
maxCharsinteger
default 200000
Cut the inline Markdown after this many characters, with a note (a file written with "output" is never cut). Default 200000.
outputstringWrite the Markdown to this file (or folder ending in "/") and return its path instead of the inline text.

Example

{
  "html": "<h1>Notes</h1><p>Hello <b>world</b>, see <a href=\"/docs\">docs</a>.</p><ul><li>one</li><li>two</li></ul>",
  "baseUrl": "https://example.com/"
}

Result

{
  "markdown": "# Notes\n\nHello **world**, see [docs](https://example.com/docs).\n\n- one\n- two\n"
}

Example

{
  "path": "page.html",
  "readable": true,
  "output": "page.md"
}
ai_image_diffCompare two images

Compare two images pixel by pixel (pixelmatch, anti-aliasing aware) and write a diff PNG with changed pixels in red over a faded copy of the first image. Returns the number and percentage of different pixels. Images must be the same size unless resize is true (the second is stretched to the first's size). Reads PNG, JPEG, WebP, AVIF, GIF (first frame), TIFF and SVG; file path or base64; up to 50 MP. Good for visual regression checks.

InputTypeDescription
a requiredstringThe first (reference) image: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
b requiredstringThe second image: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
thresholdnumber
default 0.1
Colour difference tolerance 0..1; smaller is stricter. Default 0.1.
includeAAboolean
default false
Count anti-aliased edge pixels as differences too. Default false.
resizeboolean
default false
Resize the second image to the first one's size when they differ (otherwise that is an error).
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example: Writes before-diff.png and returns the changed pixel count.

{
  "a": "before.png",
  "b": "after.png",
  "threshold": 0.1
}
ai_json_schema_inferInfer a JSON Schema from samples

Generate a JSON Schema (draft 2020-12) from one or more sample JSON values: types (integer vs number, mixed types as a type list), "null" added where a value can be null, required = keys present in every sample object, array items merged across all elements, string formats guessed when every sample matches (date-time, date, email, uri, uuid), and optional enums for small repeated string sets. Give "samples" (an array of sample values), or "text" (JSON, or JSONL = one sample per line), or "file" (path or base64 of a .json/.jsonl file). An array sample describes an array; to treat its elements as separate samples set eachItem. Pure local code; optionally writes the schema to "output".

InputTypeDescription
samplesobject[]Sample values; each element is one sample.
textstringOr: JSON text (one sample) or JSONL (one sample per line).
filestringOr: a .json/.jsonl file (local path or base64).
eachItemboolean
default false
If the only sample is an array, use its elements as the samples (schema of one record). Default false.
titlestringOptional "title" for the schema.
enumsboolean
default false
Emit "enum" for string fields with few distinct values that repeat. Default false.
maxEnumValuesinteger
default 8
Largest string set turned into an enum (default 8).
formatsboolean
default true
Guess string formats (default true).
requiredboolean
default true
List keys present in every sample as "required" (default true).
additionalPropertiesboolean
default true
false adds additionalProperties: false to every object (strict). Default true (left open).
outputstringOptional: also write the schema as a .json file here.

Example

{
  "samples": [
    {
      "id": 1,
      "email": "a@b.co",
      "tags": [
        "x"
      ]
    },
    {
      "id": 2,
      "email": "c@d.org",
      "tags": [],
      "note": null
    }
  ]
}

Result

{
  "samples": 2,
  "schema": {
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "type": "object",
    "properties": {
      "id": {
        "type": "integer"
      },
      "email": {
        "type": "string",
        "format": "email"
      },
      "tags": {
        "type": "array",
        "items": {
          "type": "string"
        }
      },
      "note": {
        "type": "null"
      }
    },
    "required": [
      "id",
      "email",
      "tags"
    ]
  }
}
ai_json_schema_validateValidate JSON against a JSON Schema

Validate JSON data against a JSON Schema with Ajv, reporting every error (allErrors) with its instancePath (JSON Pointer), schemaPath, keyword, message and params. Draft 2020-12 is used when the schema's $schema says 2020-12 (2019-09 likewise), otherwise draft-07 (draft-04/06 schemas are checked as draft-07). Formats checked: date, time, date-time, email, hostname, ipv4, ipv6, uri, uri-reference, uuid, regex, json-pointer (other formats are ignored and listed). Data: inline "data" (any JSON value), "dataText" (JSON text) or "dataFile" (path or base64). Schema: inline "schema" (object or JSON text) or "schemaFile". Remote $ref URLs are never fetched. Runs locally.

InputTypeDescription
dataobjectThe JSON value to validate, inline (a string here is validated as a string; use dataText for JSON text).
dataTextstringOr: the data as JSON text.
dataFilestringOr: a JSON file (local path or base64).
schemaobject | boolean | stringThe JSON Schema inline: an object (or JSON text).
schemaFilestringOr: a JSON Schema file (local path or base64).
maxErrorsinteger
default 100
Most errors to return (default 100).

Example

{
  "data": {
    "name": "Omar",
    "age": -1
  },
  "schema": {
    "type": "object",
    "required": [
      "name",
      "email"
    ],
    "properties": {
      "age": {
        "type": "integer",
        "minimum": 0
      }
    }
  }
}

Result

{
  "valid": false,
  "errorCount": 2,
  "draft": "draft-07"
}
ai_markdown_to_docxMarkdown to Word (.docx)

Convert Markdown (GitHub flavoured) to a real Word .docx: headings H1-H6 (Word heading styles, so the navigation pane and table of contents work), paragraphs with bold, italic, strikethrough, inline code and clickable links, bullet and numbered lists (nested, numbering restarts per list, task-list checkboxes), tables (header row shaded, column alignment kept), blockquotes, fenced code blocks in a monospace font, horizontal rules, and a page break wherever a line says \newpage. Arabic/Hebrew paragraphs are laid out right-to-left automatically (rtl: "auto"). Images are not embedded (their alt text is shown); raw HTML tags are dropped. Options: title and author (document properties), font (default Calibri), fontSize. Give "markdown" inline or "path" to a .md file. Writes the .docx to "output" (default name from the title or first heading).

InputTypeDescription
markdownstringThe Markdown text inline.
pathstringOr: a Markdown file (local path or base64).
titlestringDocument title (file properties, and the default file name).
authorstringAuthor shown in the file properties.
fontstring
default "Calibri"
Body font (default Calibri).
fontSizenumber
default 11
Body font size in points (default 11).
rtl"auto" | "on" | "off"
default "auto"
Right-to-left paragraphs: "auto" (detect Arabic/Hebrew per paragraph), "on" or "off".
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example

{
  "markdown": "# Report\n\nHello **world**.\n\n- one\n- two",
  "title": "Report"
}

Result

{
  "path": "C:\\Users\\User\\AppData\\Local\\Temp\\s3eidat-docs-7XnoQD\\Report.docx",
  "bytes": 9792,
  "mimeType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
  "paragraphs": 4
}
ai_markdown_to_pdfMarkdown to PDF

Turn Markdown (GitHub-flavoured) into a clean, paginated A4/Letter/A5 PDF: headings, paragraphs with word wrap, bold/italic/strikethrough, inline code, links (clickable), bullet / numbered / task lists (nested), tables (wrapped cells, header repeated on new pages), blockquotes, code blocks, horizontal rules, local images (relative to the Markdown file; remote images are never fetched and show as alt text), page breaks (\pagebreak, \newpage or <!-- pagebreak -->), page numbers, margins and title metadata. Text uses the built-in PDF fonts: Latin text only (English and Western European). Arabic, CJK and other scripts are not supported yet: such characters are replaced with "?" (or rejected with onUnsupportedChar: "error"); emoji are dropped; arrows and common symbols are transliterated. Give "markdown" inline or "path" to a .md file.

InputTypeDescription
markdownstringMarkdown text. Use this or "path".
pathstringA .md file: local path or base64. Relative image links resolve against its folder.
titlestringPDF title metadata. Default: the first # / ## heading.
authorstring
paper"A4" | "Letter" | "A5"
default "A4"
landscapeboolean
default false
marginnumber
default 20
Page margin in mm. Default 20.
fontSizenumber
default 10.5
Body font size in pt. Default 10.5.
font"sans" | "serif"
default "sans"
Body font: sans (Helvetica) or serif (Times). Code is always Courier.
pageNumbersboolean
default true
Print "n / total" at the bottom of each page. Default true.
onUnsupportedChar"replace" | "error"
default "replace"
What to do with characters the PDF fonts can't show: replace with "?" (default) or fail.
outputstringWhere to write the result: a file path, or a folder ending in "/". Default: the server output folder (a temp folder unless --out-dir is set).
returnBase64boolean
default false
Also return the output as base64 in the result (only for small files). Default false.

Example

{
  "markdown": "# Report\n\nHello **world**.\n\n- one\n- two\n\n| a | b |\n|---|---|\n| 1 | 2 |",
  "paper": "A4"
}

Result

{
  "pages": 1,
  "title": "Report"
}

Example: Converts a Markdown file; images next to it are embedded.

{
  "path": "notes.md",
  "paper": "Letter",
  "margin": 15
}
ai_media_infoAudio/video metadata

Read audio and video file metadata offline (music-metadata): container, codec, duration, bitrate, sample rate, bit depth, channels, lossless or not, tags (title, artist, album, year, genre, track…), embedded cover art, and per-track info (including video width/height for MP4/MKV/WebM). Formats: MP3, AAC/M4A/MP4/MOV, FLAC, WAV, AIFF, OGG/Opus/Vorbis, WebM/Matroska, WMA/ASF, APE, WavPack, DSD and more. File path (streamed, any size) or base64. Duration of VBR MP3s without a header is computed by scanning the file.

InputTypeDescription
file requiredstringThe audio or video file: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).

Example: Duration, bitrate, codec, ID3 tags and cover art presence.

{
  "file": "song.mp3"
}
ai_ocrOCR (image or scanned PDF to text)

Read the text in an image (PNG, JPEG, WebP, TIFF incl. multi-page, GIF, AVIF) or a scanned PDF with Tesseract (tesseract.js), fully offline: the English and Arabic models ship with the server and nothing is downloaded or uploaded. Languages: "eng", "ara" or "eng+ara" (default). PDF pages are rendered at 200 dpi by default. Returns the text per page and the mean word confidence (0-100); give "output" to also write a .txt file. Limits: up to 200 pages per call, roughly 1-5 s per page; printed text works well, handwriting and very small or skewed text poorly. For PDFs that already have a text layer, pdf_to_text is faster and exact. Give "file" as a local path or base64.

InputTypeDescription
file requiredstringThe image or PDF: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
languages"eng" | "ara" | "eng+ara"
default "eng+ara"
Languages to read. Default "eng+ara".
pagesstringPDF pages / TIFF frames, e.g. "1-3, 5" (default all).
dpiinteger
default 200
Render resolution for PDF pages. Default 200.
psm"auto" | "block" | "line" | "word" | "sparse"
default "auto"
Page layout: "auto" (default), "block" (one uniform block), "line", "word", or "sparse" (scattered text).
perPageboolean
default false
Also return the text of each page separately in "pages". Default false (the joined "text" has page separators).
outputstringAlso write the text to this .txt file (or folder ending in "/").
maxCharsinteger
default 200000
Cut the inline text after this many characters (the file is never cut).

Example

{
  "file": "scan.png",
  "languages": "eng"
}

Example

{
  "file": "scanned-contract.pdf",
  "pages": "1-3",
  "output": "contract.txt"
}
ai_pdf_to_markdownPDF to Markdown

Convert a text PDF to structured Markdown: headings guessed from font size (text clearly larger than the body becomes #, ##, ###), paragraphs rebuilt from line spacing with hyphenated words mended, bullet and numbered lists kept, right-to-left lines in reading order. Optional page range ("1-5, 8") and page markers. Scanned PDFs without a text layer give empty pages (listed in pagesWithoutText; run OCR first). Tables and columns are read line by line. Accepts a local path or base64. Inline Markdown is cut at maxChars (default 200k); "output" saves it to a file.

InputTypeDescription
file requiredstringThe PDF: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
pagesstringPages to convert, e.g. "1-3, 7" or "5-" (1-based). Default: all.
passwordstringPassword, if the PDF is protected.
pageMarkersboolean
default false
Put "<!-- Page N -->" before each page and "---" between pages. Default false.
maxCharsinteger
default 200000
Cut the inline Markdown after this many characters, with a note (a file written with "output" is never cut). Default 200000.
outputstringWrite the Markdown to this file (or folder ending in "/") and return its path instead of the inline text.

Example

{
  "file": "paper.pdf"
}

Example

{
  "file": "book.pdf",
  "pages": "1-20",
  "pageMarkers": true,
  "output": "book.md"
}
ai_pptx_to_markdownPowerPoint (.pptx) to Markdown

Extract the text of a PowerPoint .pptx deck as Markdown: one "## Slide N: title" section per slide in presentation order, text boxes as paragraphs and bullet lists (with indent levels), tables as Markdown tables, and speaker notes as a quote under each slide. Images, charts and SmartArt text are not extracted. Old binary .ppt is not supported. Optional "slides" range (e.g. "1-5, 8"). Accepts a local path or base64. Inline Markdown is cut at maxChars (default 200k); "output" saves it to a file. Runs locally; nothing is uploaded.

InputTypeDescription
file requiredstringThe .pptx presentation: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
slidesstringSlides to include, e.g. "1-3, 7" or "5-" (1-based). Default: all.
notesboolean
default true
Include speaker notes. Default true.
maxCharsinteger
default 200000
Cut the inline Markdown after this many characters, with a note (a file written with "output" is never cut). Default 200000.
outputstringWrite the Markdown to this file (or folder ending in "/") and return its path instead of the inline text.

Example

{
  "file": "pitch.pptx"
}

Example

{
  "file": "lecture.pptx",
  "slides": "1-10",
  "notes": false
}
ai_sql_querySQL query over tables

Run one read-only SQL SELECT (alasql dialect: JOIN, GROUP BY, HAVING, ORDER BY, LIMIT, UNION, WITH, subqueries, CASE, SUM/COUNT/AVG/MIN/MAX/MEDIAN/STDEV, string and date functions) over one or more tables. Each table is { name, file | csv | json }: a local path or base64 of a CSV, TSV, JSON array, JSONL or .xlsx (first sheet, or "sheet"), or inline CSV text / JSON rows. CSV cells are typed (numbers, true/false, empty → null; ISO dates stay ISO text, so they sort and compare correctly; YEAR()/MONTH() work on them). Only SELECT is allowed: INSERT/UPDATE/DELETE/DROP/CREATE/ALTER/ATTACH, SELECT INTO, file readers like FROM CSV(...), inline JavaScript and the -> operator are refused. Table names are case-sensitive; quote odd column names and reserved words (total, value, count, date…) as [My Column]. Returns columns and up to maxRows rows (default 500) with the total count; set "output" (.csv, .tsv, .json, .jsonl or .xlsx) to save every row to a file. Runs locally; nothing is uploaded.

InputTypeDescription
query requiredstringOne SELECT statement, e.g. SELECT region, SUM(amount) AS revenue FROM sales GROUP BY region ORDER BY revenue DESC
tables requiredobject[]The tables the query can read (1-20).
typesboolean
default true
Type CSV cells (numbers, booleans, null). Default true; false keeps every CSV cell as text.
maxRowsinteger
default 500
Rows to return inline (default 500). "total" always counts all rows.
outputstringOptional: save ALL result rows to this file; the type comes from the extension (.csv, .tsv, .json, .jsonl, .xlsx; default .csv).

Example

{
  "query": "SELECT region, SUM(amount) AS revenue FROM sales GROUP BY region ORDER BY revenue DESC",
  "tables": [
    {
      "name": "sales",
      "csv": "region,amount\nNorth,10\nSouth,5\nNorth,7.5"
    }
  ]
}

Result

{
  "columns": [
    "region",
    "revenue"
  ],
  "rows": [
    {
      "region": "North",
      "revenue": 17.5
    },
    {
      "region": "South",
      "revenue": 5
    }
  ],
  "total": 2,
  "truncated": false
}
ai_text_similarityText similarity and near-duplicates

Compare two or more texts (or text files) for overlap. For every pair returns: Jaccard similarity of word shingles (k=3 words by default), a MinHash estimate of that Jaccard (128 hashes), and cosine similarity of word frequencies. Words are lower-cased, punctuation and Arabic diacritics ignored. Measures wording overlap, not meaning (no embeddings): paraphrases score low. Mode "pairs" (default) lists pairs sorted by the chosen metric; mode "dedupe" groups near-duplicates whose metric is at least "threshold" (default 0.8; groups are connected, so A~B and B~C put A, B, C together). Give "texts" (inline strings) and/or "paths" (file paths or base64). Up to 1000 texts.

InputTypeDescription
textsstring[]Texts to compare (inline).
pathsstring[]Or/also: UTF-8 text files (local paths or base64).
labelsstring[]Optional names for the texts, in order (texts first, then paths). Default: "#1", "#2"… or the file name.
mode"pairs" | "dedupe"
default "pairs"
"pairs" (similarity of each pair) or "dedupe" (groups of near-duplicates).
metric"jaccard" | "minhash" | "cosine"
default "jaccard"
Metric used to sort pairs and to group duplicates. Default jaccard.
thresholdnumber
default 0.8
Dedupe: pairs at or above this are duplicates. Default 0.8.
shingleSizeinteger
default 3
Words per shingle (k). Default 3.
numHashesinteger
default 128
MinHash signature length. Default 128 (error about ±0.09).
maxPairsinteger
default 200
Most pairs to return (highest first). Default 200.

Example: Pair #1/#2 scores 1 on every metric.

{
  "texts": [
    "the cat sat on the mat",
    "the cat sat on the mat",
    "a completely different sentence here"
  ]
}

Result

{
  "count": 3,
  "texts": [
    {
      "index": 0,
      "label": "#1",
      "words": 6,
      "shingles": 4
    },
    {
      "index": 1,
      "label": "#2",
      "words": 6,
      "shingles": 4
    },
    {
      "index": 2,
      "label": "#3",
      "words": 5,
      "shingles": 3
    }
  ],
  "pairCount": 3,
  "pairs": [
    {
      "a": 0,
      "b": 1,
      "jaccard": 1,
      "minhash": 1,
      "cosine": 1,
      "labelA": "#1",
      "labelB": "#2"
    },
    {
      "a": 0,
      "b": 2,
      "jaccard": 0,
      "minhash": 0,
      "cosine": 0,
      "labelA": "#1",
      "labelB": "#3"
    },
    {
      "a": 1,
      "b": 2,
      "jaccard": 0,
      "minhash": 0,
      "cosine": 0,
      "labelA": "#2",
      "labelB": "#3"
    }
  ]
}

Example: One group: #1 and #2.

{
  "texts": [
    "Hello world, this is a test.",
    "hello world this is a test",
    "Something else entirely."
  ],
  "mode": "dedupe"
}

Result

{
  "count": 3,
  "texts": [
    {
      "index": 0,
      "label": "#1",
      "words": 6,
      "shingles": 4
    },
    {
      "index": 1,
      "label": "#2",
      "words": 6,
      "shingles": 4
    },
    {
      "index": 2,
      "label": "#3",
      "words": 3,
      "shingles": 1
    }
  ],
  "pairCount": 3,
  "groups": [
    {
      "members": [
        0,
        1
      ],
      "labels": [
        "#1",
        "#2"
      ],
      "keep": 0,
      "minSimilarity": 1
    }
  ],
  "duplicateCount": 1,
  "uniqueCount": 2,
  "pairs": [
    {
      "a": 0,
      "b": 1,
      "jaccard": 1,
      "minhash": 1,
      "cosine": 1,
      "labelA": "#1",
      "labelB": "#2"
    }
  ]
}
ai_token_countCount tokens

Count LLM tokens in a text or text file with the OpenAI tiktoken tokenizers ("o200k_base", default, used by GPT-4o and the o-series; or "cl100k_base", used by GPT-4/3.5 and text-embedding-3). Also returns characters and words, and whether the text fits in common context windows (8k, 32k, 128k, 200k, 1M: e.g. "fits in 200k: yes"). Claude's tokenizer is not public, so for Claude the count is an estimate (typically within ~10-20%; Claude usually needs a little more), returned as a rough range. Runs locally, no API calls. Give "text" or "path" (file path or base64). Large texts (many MB) take a few seconds.

InputTypeDescription
textstringThe text (inline).
pathstringOr: a UTF-8 text/markdown file, as a local path or base64 (raw or a data: URI).
encoding"o200k_base" | "cl100k_base"
default "o200k_base"
Tokenizer: "o200k_base" (GPT-4o / o-series, default) or "cl100k_base" (GPT-4 / GPT-3.5 / text-embedding-3).

Example

{
  "text": "hello world"
}

Result

{
  "tokens": 2,
  "words": 2,
  "characters": 11
}

Example

{
  "path": "notes.md",
  "encoding": "cl100k_base"
}
ai_web_fetchFetch a web page as Markdown

Fetch a web page (http/https) and return its readable content as clean Markdown, with title, byline, final URL after redirects and a word count. Uses Mozilla Readability to keep the main article (falls back to the whole page), or a CSS "selector" you give. Respects robots.txt (refuses pages disallowed for the s3eidat-tools-mcp user agent), only reads HTML and text responses, stops at 5 MB and 20 seconds, follows up to 5 redirects, and refuses private, local and loopback addresses unless allowPrivate is true. Does not run JavaScript, so pages that build their content in the browser may come back nearly empty. Inline Markdown is cut at maxChars (default 200k); "output" saves it to a file.

InputTypeDescription
url requiredstringThe page to fetch (http:// or https://).
selectorstringCSS selector: convert only the matching elements instead of the Readability main content.
readableboolean
default true
Keep only the main content (default true). False converts the whole page body.
includeLinksboolean
default true
Keep links (made absolute) and images. False turns links into plain text and drops images.
allowPrivateboolean
default false
Allow private, local and loopback hosts (e.g. a dev server on localhost). Default false.
maxCharsinteger
default 200000
Cut the inline Markdown after this many characters, with a note (a file written with "output" is never cut). Default 200000.
outputstringWrite the Markdown to this file (or folder ending in "/") and return its path instead of the inline text.

Example

{
  "url": "https://en.wikipedia.org/wiki/Markdown"
}

Example

{
  "url": "https://example.com/docs/page",
  "selector": "main",
  "includeLinks": false
}
ai_xlsx_to_markdownExcel (.xlsx/.csv) to Markdown tables

Turn an Excel .xlsx workbook (or a .csv/.tsv file) into Markdown tables, one "## sheet" section per sheet, or only the sheet you name. The first used row is the table header; empty rows and columns around the data are dropped. Formulas show their cached result (as last saved by Excel), dates are ISO (2026-10-30, or 2026-10-30T14:05:00), merged cells show their value once, hyperlinks become Markdown links. Each sheet shows at most maxRows data rows (default 200) and says how many were left out. Old binary .xls is not supported. Accepts a local path or base64. Runs locally.

InputTypeDescription
file requiredstringThe .xlsx or .csv file: a local file path (absolute, or relative to the server's --root) or base64 data (raw or a data: URI).
sheetstringOnly this sheet: its name, or its 1-based position (e.g. "2"). Default: every sheet.
maxRowsinteger
default 200
Data rows to show per sheet (after the header). Default 200.
format"auto" | "xlsx" | "csv"
default "auto"
Input format. "auto" tells .xlsx (a ZIP) from CSV by its bytes.
maxCharsinteger
default 200000
Cut the inline Markdown after this many characters, with a note (a file written with "output" is never cut). Default 200000.
outputstringWrite the Markdown to this file (or folder ending in "/") and return its path instead of the inline text.

Example

{
  "file": "budget.xlsx"
}

Example

{
  "file": "sales.xlsx",
  "sheet": "Q3",
  "maxRows": 50
}

Example

{
  "file": "contacts.csv"
}
ai_zipZIP create / extract / list

Work with ZIP archives locally. action "create": zip files and folders (given as paths; folders keep their own name as the top folder, symlinks are skipped) into one .zip. action "extract": unpack a .zip (path or base64) into the "output" folder; entries with "..", absolute paths or drive letters are refused (zip-slip protection), existing files are kept unless overwrite is true. action "list": entry names with sizes, no extraction. Limits: total uncompressed size 500 MB and 10000 files by default (maxTotalMB / maxFiles). Encrypted ZIPs are not supported.

InputTypeDescription
action required"create" | "extract" | "list"
filesstring[]create: files and folders to add (local paths).
zipstringextract / list: the ZIP archive, a local path or base64.
outputstringcreate: the .zip path (or a folder ending in "/"). extract: the folder to extract into. Default: the server output folder.
levelinteger
default 6
create: compression level 0 (store) to 9 (smallest). Default 6.
overwriteboolean
default false
extract: replace files that already exist in the output folder. Default false (they are skipped).
maxTotalMBnumber
default 500
Cap on the total uncompressed size, MB. Default 500.
maxFilesinteger
default 10000
Cap on the number of entries. Default 10000.

Example

{
  "action": "create",
  "files": [
    "/projects/site",
    "/notes/readme.txt"
  ],
  "output": "/backups/site.zip"
}

Example

{
  "action": "list",
  "zip": "/downloads/archive.zip"
}

Example

{
  "action": "extract",
  "zip": "/downloads/archive.zip",
  "output": "/downloads/archive/"
}