On this page
- Overview
- Quick start
- How to use the app
- Logo placement and size
- Background and optional text
- How automatic placement works for users
- Output formats and download
- Recommended watermark settings
- Features and supported files
- Privacy, storage, and metadata
- Browser support and limitations
- Developer documentation and architecture
- Canvas rendering and layout calculations
- Automatic placement algorithm
- Cookie, localStorage, and IndexedDB model
- Deployment and repository structure
- Security and privacy model
- FAQ
Overview
This app is designed for a common production task: add the same brand logo to different photos quickly, keep the watermark consistent, and download a finished image without opening a full desktop editor.
The user chooses one photo and one logo. The app places the logo in a selected corner, resizes it proportionally, adds optional text, and can draw a transparent black or white background behind the complete watermark block. All controls update the preview immediately.
The tool works with landscape, portrait, and square images. Manual controls give exact placement, while the automatic placement option chooses a practical size, margin, and corner based on the photo orientation and visual detail near the edges.
This is a local browser tool, not a cloud image service. The selected photo and logo are decoded and rendered on the user's device. Only the user can download the finished result.
Quick start
- Open the Image Watermark app.
- Choose or drag a photo into the first input area.
- Choose or drag a logo into the second input area.
- Click Auto place logo, or choose a corner and enter exact pixel distances.
- Adjust logo size, background, text, font, and color if needed.
- Choose the output format and click Download image.
The last settings and logo are remembered in the same browser, so repeat work usually starts with only one action: choose the next photo.
How to use the app
Step 1. Choose a photo
Click the photo area and select a local image, or drag one image from the desktop into the area. The app shows the file name, pixel resolution, file size, and detected format.
The preview canvas uses the photo's natural pixel dimensions. The app does not intentionally reduce the output resolution.
Step 2. Choose a logo
Select the logo that should appear on the photo. A transparent PNG, transparent WebP, or SVG usually gives the cleanest result. JPG logos also work, but the original JPG background remains visible.
After a logo is loaded, the app stores a local copy in browser storage. It is restored automatically during the next visit on the same site and browser profile. The Clear saved logo button removes both the current logo and the stored copy.
Step 3. Choose manual or automatic placement
For manual placement, select one of four corners:
Top left Top right Bottom left Bottom right
The two pixel inputs change their labels to match the selected corner. For example, bottom right uses distance from the right edge and distance from the bottom edge.
For automatic placement, click Auto place logo after both a photo and logo are loaded. The app analyses the four corners and updates the corner, size, and margin values.
Step 4. Adjust the watermark appearance
Set the logo width as a percentage of the photo width. Add a white or black transparent background when the logo needs better separation from a detailed photo. The background padding is measured relative to the logo width, so it remains proportional on different image sizes.
Two optional text lines can appear below the logo. Both lines use the same font family, size, color, and weight. Empty lines are ignored, and the background automatically expands for the text that is present.
Step 5. Download the finished image
Review the full composition in the live preview, choose a file name and output format, then click Download image. The downloaded image contains the photo and complete watermark as one new image file.
Logo placement and size
Corner anchoring
The selected corner acts as the anchor for the full watermark block, including background and text. Pixel distances are measured from the chosen horizontal and vertical edges to the outside of that block.
This means a 30 pixel bottom distance keeps the complete background 30 pixels above the bottom edge. It does not place only the logo image 30 pixels above the edge while allowing the background to extend farther.
Logo width percentage
Logo size is controlled as a percentage of the photo width, from 2% to 60%. The logo height is calculated from the original logo aspect ratio, so the image is never intentionally stretched.
A percentage is more useful than one fixed pixel width when the tool is used with many photo sizes. For example, an 18% logo is 720 pixels wide on a 4000 pixel photo and 216 pixels wide on a 1200 pixel photo.
Why the displayed percentage may be limited internally
The rendering code prevents the watermark from becoming wider than the usable photo area. If very large logo size, padding, or edge distances would push the block outside the image, the actual rendered logo is constrained so the block can fit horizontally.
Background and optional text
Background choices
The watermark background has three modes:
- None: the logo and text are drawn directly on the photo.
- White: a white rounded rectangle is drawn behind the logo and text.
- Black: a black rounded rectangle is drawn behind the logo and text.
Opacity
Background opacity can be set from 0% to 100%. Low values create a subtle highlight, while high values give stronger visual separation. The logo and text remain fully opaque.
Background padding
Padding can be set from 3% to 40% of the logo width. It is applied around the full content block. The minimum is above zero because the purpose of the background is to remain visibly larger than the logo.
Optional text
Line 1 and line 2 are placed below the logo and centered inside the same background. Examples include Photo made by and www.casinolove.org.
The text size is measured as a percentage of the logo width. If a line would be too wide for the photo, the app reduces the effective font size so the text can fit.
Font choices use common browser and operating-system fonts. This avoids network font downloads and keeps the app self-contained.
How automatic placement works for users
Automatic placement is a practical helper. It does not use a cloud AI service and does not send the photo anywhere. It examines a small local copy of the photo and compares the four corners.
The automatic action changes three types of setting:
- logo size, based on photo orientation
- horizontal and vertical margins, based on photo dimensions
- corner, based on visual detail and optional browser face detection
Automatic placement is a suggestion, not a guarantee. Always inspect the preview before downloading, especially when a photo contains faces, products, captions, or important details near several corners.
Output formats and download
The app can export the finished image as PNG, JPG, or WebP.
| Output choice | Behavior | Typical use |
|---|---|---|
| Same when possible | JPG stays JPG, WebP stays WebP, and other source types use PNG. | Fast repeat work with a practical default. |
| JPG | Lossy browser JPEG export. The quality value is used. | Normal photos, broad compatibility, smaller files. |
| PNG | Lossless PNG export. The quality value is not used. | Graphics, sharp text, screenshots, maximum pixel consistency. |
| WebP | Browser WebP export. The quality value is used. | Modern web publishing and smaller photo files. |
Output file name
When a photo is loaded, the app removes its existing extension and adds -watermarked. Characters that are not safe in common file names are replaced before download.
Quality setting
JPG and WebP support a quality value from 50% to 100%. The default is 92%. PNG hides the quality field because the browser's PNG canvas output is lossless.
Recommended watermark settings
Subtle photo credit
- logo width: 10% to 16%
- background: black or white
- opacity: 30% to 50%
- padding: 6% to 10%
Strong brand mark
- logo width: 16% to 24%
- background opacity: 50% to 75%
- padding: 8% to 14%
- use a short URL below the logo
Clean transparent logo
- background: none
- choose an uncluttered corner
- keep strong contrast with the photo
- inspect every image before export
Features and supported files
Core user features
- single-photo workflow
- drag-and-drop photo and logo inputs
- live full-resolution canvas preview
- four corner anchors
- exact pixel edge distances
- proportional logo size control
- automatic corner, size, and margin selection
- transparent white or black background
- two optional text lines
- font family, size, color, and bold controls
- PNG, JPG, and WebP download
Product decisions
- no image upload
- no server-side image processing
- no registration
- no advertisements inside the app
- no external JavaScript or CSS libraries
- single self-contained application file
- desktop-focused layout
- settings remembered for repeat work
- last logo restored locally
| Area | Supported behavior | Important note |
|---|---|---|
| Photo input | Browser-supported image files through image/* | JPG, PNG, WebP, BMP, and SVG work in modern browsers. Other formats depend on browser decoding. |
| Logo input | PNG, WebP, SVG, and JPG | Transparent PNG, WebP, or SVG is recommended. |
| Output | PNG, JPG, and WebP | Encoding is provided by the browser canvas implementation. |
| Photo count | One photo at a time | The same logo and settings remain ready for the next photo. |
| Resolution | Source pixel dimensions are preserved | Very large images are still limited by browser memory and maximum canvas dimensions. |
Privacy, storage, and metadata
No photo upload
The app does not contain a network upload action. File inputs provide local browser objects, and the images are decoded through object URLs on the user's device.
What is remembered
Small settings are stored in a cookie and localStorage. The actual logo file is stored in IndexedDB because normal cookies are far too small for image files. The selected photo is not stored for the next session.
Image metadata
The app creates a new image from canvas pixels. Users should not expect original EXIF data, camera information, GPS information, embedded thumbnails, or other source metadata to remain in the downloaded file.
Color management
Image decoding and canvas export are handled by the browser. Color profiles, HDR information, and special source color spaces may be converted or lost. This tool is intended for normal web and social image preparation, not color-critical archival work.
Browser support and limitations
A modern desktop browser is recommended. Chrome, Edge, Firefox, Safari, and similar modern browsers support the main canvas workflow, but exact image decoding, WebP export, IndexedDB behavior, and download behavior can vary.
Main browser requirements
- HTML file inputs
- Blob and object URL support
- Canvas 2D rendering
- canvas toBlob() export
- IndexedDB
- localStorage and cookies for repeat settings
- requestAnimationFrame for efficient preview updates
Important limitations
- one source photo is processed at a time
- there is no free drag positioning inside the preview
- automatic placement examines corners only
- automatic face detection is optional and browser-dependent
- the app does not preserve source metadata
- very large images can use significant browser memory
- unsupported image formats cannot be decoded
- the app does not claim that a watermark cannot be removed or edited later
Developer documentation and architecture
Technology stack
- semantic HTML5 for application structure
- custom CSS for the complete interface
- vanilla JavaScript for state, image handling, rendering, storage, and export
- Canvas 2D for the final photo composition
- cookies, localStorage, and IndexedDB for browser persistence
- no framework, build system, package manager, or runtime dependency
- the official CasinoLove site logo embedded as a WebP data URL in the app
Why the app is a single file
The application CSS, markup, JavaScript, and CasinoLove header logo are included in one HTML file. This makes internal distribution, static hosting, review, backup, and open-source inspection simple. It also prevents the interface from failing because a third-party library or CDN is unavailable.
High-level application flow
page load
-> read settings cookie or localStorage fallback
-> sanitize and apply settings
-> bind native input and button events
-> restore the last logo from IndexedDB
photo selected
-> validate image MIME type
-> create an object URL
-> decode with an Image element
-> set the canvas to the natural photo dimensions
-> render photo and current watermark
control changed
-> update central settings state
-> synchronize matching range and number inputs
-> queue one requestAnimationFrame render
-> save settings after a short debounce
download clicked
-> render the current full-resolution canvas
-> encode with canvas.toBlob()
-> create a temporary object URL
-> trigger a browser download
Central runtime state
The runtime keeps separate references for the decoded photo, decoded logo, original File objects, and their object URLs. Settings are stored in one plain object so every render uses one consistent snapshot.
state = {
photo,
logo,
photoFile,
logoFile,
photoUrl,
logoUrl
}
settings = {
corner,
offsetX,
offsetY,
logoWidthPct,
background,
bgOpacity,
bgPaddingPct,
text1,
text2,
fontFamily,
fontSizePct,
textColor,
bold,
exportFormat,
exportQuality
}
Canvas rendering and layout calculations
Canvas size
The canvas width and height are set to the decoded photo's natural dimensions. CSS scales that canvas for the desktop preview, but the internal pixel buffer remains at source resolution.
Logo dimensions
The requested logo width is calculated from the photo width and selected percentage. Logo height is calculated from the source logo aspect ratio.
requestedLogoWidth = photoWidth * logoWidthPct / 100
logoHeight = logoWidth * sourceLogoHeight / sourceLogoWidth
Padding and block size
When a background is enabled, padding is calculated from logo width. The content width is the larger of the logo width and widest text line. The content height includes logo height, a logo-to-text gap, and one line-height for every non-empty text line.
padding = logoWidth * bgPaddingPct / 100
contentWidth = max(logoWidth, widestTextWidth)
boxWidth = contentWidth + padding * 2
boxHeight = logoHeight + textAreaHeight + padding * 2
Text measurement
Canvas measureText() is used before drawing. The chosen text size is proportional to logo width. If the widest line exceeds the available photo width, the effective font size is reduced and measured again.
Corner coordinates
Left corners use the horizontal offset directly. Right corners subtract the offset and watermark width from the photo width. Top corners use the vertical offset directly. Bottom corners subtract the offset and watermark height from the photo height.
leftX = offsetX
rightX = photoWidth - offsetX - boxWidth
topY = offsetY
bottomY = photoHeight - offsetY - boxHeight
Draw order
- clear the canvas
- draw the source photo
- draw the rounded background with selected opacity
- draw the logo at full opacity
- draw each text line at full opacity
Automatic placement algorithm
Orientation-based size
The photo width-to-height ratio determines the initial logo width percentage.
| Photo aspect ratio | Recommended logo width | Typical shape |
|---|---|---|
| 1.55 or wider | 15% | wide landscape |
| 1.15 to below 1.55 | 17% | normal landscape |
| 0.88 to below 1.15 | 19% | square or nearly square |
| 0.68 to below 0.88 | 23% | normal portrait |
| below 0.68 | 27% | tall portrait |
Automatic margin
The automatic margin is 2.6% of the shorter photo dimension, rounded to a whole pixel and constrained between 16 and 120 pixels.
margin = round(clamp(min(photoWidth, photoHeight) * 0.026, 16, 120))
Corner analysis image
A temporary canvas is created for analysis only. Its longest side is at most 360 pixels. This reduces CPU and memory use while keeping enough visual information to compare the corners.
Visual detail score
Each candidate corner is scored inside the expected watermark rectangle. The score combines luminance variation and luminance differences between nearby samples. Lower scores represent calmer areas that are usually better for a watermark.
detailScore = standardDeviation(luminance) * 0.65
+ averageNeighborDifference * 1.35
A small deterministic preference keeps results stable when several corners have nearly equal detail. The base order is bottom right, bottom left, top right, then top left.
Optional face penalty
If the browser exposes the experimental FaceDetector API, detected face rectangles are compared with each candidate watermark rectangle. A large penalty is added when they overlap. If the API is unavailable or detection fails, the app continues normally using visual detail only.
Why this is described as smart placement instead of AI
The algorithm is deterministic local image analysis. It does not call an AI model, does not use a remote service, and does not learn from the user's photos.
Cookie, localStorage, and IndexedDB model
| Storage | Name | Stored data | Reason |
|---|---|---|---|
| Cookie | cl_watermark_settings | Complete sanitized settings JSON | Main repeat-settings mechanism. One-year lifetime, path /, SameSite Lax. |
| localStorage | cl-watermark-settings-v1 | The same settings JSON | Fallback when cookies are blocked or unreliable. |
| IndexedDB | cl-watermark-studio | Last logo Blob and file metadata | Image files are too large for normal cookies or localStorage. |
IndexedDB structure
Database: cl-watermark-studio
Version: 1
Object store: files
Logo key: last-logo
Stored value:
{
blob,
name,
type,
lastModified
}
Settings validation
Saved values are not trusted blindly. Numeric values are converted and clamped to their allowed ranges. Corners, background modes, export formats, and font families must match known values. Text length and color syntax are also validated.
Origin behavior
Browser storage belongs to the site origin. Moving the app from one domain or subdomain to another creates a different storage area. Paths on the same origin share the cookie because it uses path /, while IndexedDB is also shared by origin.
Deployment and repository structure
Required server behavior
The server only delivers static HTML and the existing CasinoLove site assets. No PHP, Node.js, database, image processor, worker, or upload directory is required.
Site paths
https://tech.casinolove.org/add-image-watermark/
documentation page
https://tech.casinolove.org/add-image-watermark/app/
watermark application
Local testing
Normal HTTP or HTTPS is recommended because browser cookies and storage APIs behave more predictably on a real origin than under file://.
python -m http.server 8000
From the project parent directory, open http://localhost:8000/add-image-watermark/ for the documentation or http://localhost:8000/add-image-watermark/app/ for the app.
Cache considerations
The app is one HTML file, so a new deployment replaces the complete application shell. During active development, use short cache lifetimes or explicit cache invalidation for /add-image-watermark/app/index.html.
Security and privacy model
Local processing boundary
- photo and logo files come from user-controlled file inputs
- decoded images are referenced through temporary blob URLs
- object URLs are revoked when files are replaced or the page closes
- the completed image is generated in memory and downloaded locally
- the app contains no fetch, XMLHttpRequest, WebSocket, or upload code
SVG logo considerations
SVG is supported as a logo because it scales cleanly. The browser handles SVG decoding through an Image element before the result is drawn to canvas. Deployments with strict security requirements may still choose to limit accepted logo formats to PNG and WebP.
Canvas and origin safety
Local File and Blob object URLs are safe for this canvas export workflow. If developers later add remote image URLs, normal canvas cross-origin rules can mark the canvas as tainted and block export unless correct CORS headers are present.
Dependency surface
The current app has no third-party runtime dependencies. That reduces supply-chain risk and means no external script receives access to selected image files or browser storage.
FAQ
Does the app upload my photo or logo?
No. Both files stay inside the browser workflow on the user's device.
Why is my previous logo already loaded?
The last logo is stored locally in IndexedDB to speed up repeat work. Use Clear saved logo to remove it.
Is the logo stored in a cookie?
No. Cookies are normally limited to about 4 KB, which is too small for an image file. Settings use a cookie, while the logo file uses IndexedDB.
Is my previous photo stored?
No. The source photo is not restored during the next visit.
Does automatic placement use AI?
No. It uses local orientation rules and visual-detail measurements. Optional browser face detection is used only when the browser provides it.
Can I place the watermark anywhere by dragging it?
No. The current version uses one of four corner anchors with exact horizontal and vertical pixel distances.
Does the app change the photo resolution?
The canvas uses the decoded photo's natural width and height. The preview is visually scaled with CSS, but the exported pixel dimensions remain the same.
Why is the downloaded file different in size from the original?
The photo is decoded and encoded again after the watermark is drawn. File size depends on output format, quality, browser encoder, and image content.
Does it preserve EXIF and camera metadata?
No. Users should assume that original metadata is removed during canvas export.
Which logo format should I use?
A transparent PNG or WebP is the safest choice. SVG is useful when clean scaling is important and the browser can decode the file.
Why does PNG hide the quality field?
The app uses the browser's lossless PNG canvas output. The quality value does not control normal PNG output.
Can I use the app offline?
The app itself is self-contained and has no required runtime network request. After the HTML file is available locally, the editing and export workflow can work without a network connection.
Conclusion
CasinoLove Image Watermark is a focused browser tool for adding consistent brand marks and photo credits without an upload service or desktop editing application. The user interface is designed for quick repeat work, while the code remains small, inspectable, self-contained, and suitable for static hosting.