To add a watermark to an image in Laravel 13, decode both the photo and your logo with Intervention Image 4, scale the logo, and place it with $image->insert($logo, 10, 10, Alignment::BOTTOM_RIGHT, 0.5). The last arguments set the offset, the corner and the opacity. This guide shows the method, a reusable service, and how to apply the watermark to uploads.
Targets: Laravel 13 (PHP 8.3 to 8.5), intervention/image 4 and the GD or Imagick extension.
Step 1: Install and prepare the logo
composer require intervention/imageSave your watermark as a PNG with a transparent background, for example resources/images/watermark.png. A PNG keeps the transparent areas, so only the logo shape is drawn on the photo.
Step 2: Understand insert()
The method in Intervention Image 4 has this signature:
Image::insert(mixed $image, int $x = 0, int $y = 0, string|Alignment $alignment = Alignment::TOP_LEFT, float $transparency = 1)$imageis the image to insert: a path, binary data or another image object.$xand$yare offsets from the chosen corner.$alignmentis where the image is placed (for exampleAlignment::BOTTOM_RIGHT).$transparencygoes from 0 (invisible) to 1 (opaque).
Step 3: Write a watermark service
<?php
namespace App\Services;
use Intervention\Image\Alignment;
use Intervention\Image\Drivers\Gd\Driver;
use Intervention\Image\ImageManager;
class WatermarkService
{
public function apply(string $sourcePath, string $targetPath): void
{
$manager = ImageManager::usingDriver(Driver::class);
$image = $manager->decode($sourcePath);
$logoWidth = max(80, (int) ($image->width() * 0.2));
$watermark = $manager
->decode(resource_path('images/watermark.png'))
->scale(width: $logoWidth);
$image->insert($watermark, 20, 20, Alignment::BOTTOM_RIGHT, 0.5);
$image->save($targetPath);
}
}The logo is scaled to 20 percent of the photo width (at least 80 pixels), so it looks right on small and large images. The offset of 20 pixels keeps it away from the edge, and opacity 0.5 makes it visible but not distracting.
Step 4: Watermark an upload
<?php
namespace App\Http\Controllers;
use App\Services\WatermarkService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
class PhotoController extends Controller
{
public function store(Request $request, WatermarkService $watermark): RedirectResponse
{
$request->validate([
'photo' => ['required', 'image', 'mimes:jpg,jpeg,png,webp', 'max:8192'],
]);
$name = Str::uuid()->toString().'.jpg';
$folder = storage_path('app/public/photos');
if (! is_dir($folder)) {
mkdir($folder, 0775, true);
}
$watermark->apply($request->file('photo')->getRealPath(), $folder.'/'.$name);
return back()->with('success', 'Photo saved with watermark.');
}
}Saving with a .jpg extension makes Intervention encode the result as JPEG. Use .png or .webp if you prefer those formats. Keep the original upload elsewhere if you may need an unwatermarked version later.
How do I change the watermark position?
Pass another Alignment value:
$image->insert($watermark, 0, 0, Alignment::CENTER, 0.25); // faint, centered
$image->insert($watermark, 20, 20, Alignment::TOP_LEFT, 0.6); // top leftA faint centered watermark is harder to crop out than a corner logo. A corner logo is less intrusive for viewers. Choose based on your goal.
What about text watermarks?
For text, use the text() method with a font file instead of insert(), as described in the tutorial about writing text on images. You can combine both on the same image.
How do I check that it works?
- Upload a landscape photo and a portrait photo.
- Open the saved files and confirm the logo sits in the bottom right and scales with the image.
- Change the opacity to 1 and then to 0.2 to see the difference.
Common mistakes
- Logo too large: scale it to a share of the photo width instead of using a fixed size.
- JPEG logo with a white box: use a PNG with transparency.
- Opacity given as 50: the parameter range is 0 to 1, so use 0.5.
- Using version 3 methods: use
decode()and the manager factoryImageManager::usingDriver(). - Believing a watermark prevents copying: it discourages casual reuse, but it does not stop determined copying.
FAQ
Can I watermark many images at once?
Yes. Loop over the files and call the service for each. For many files, run the work in a queued job.
Does this work with Imagick?
Yes. Replace the driver import with Intervention\Image\Drivers\Imagick\Driver.
Can the logo come from storage instead of resources?
Yes. decode() accepts a path, so you can point to any readable file, or pass the binary content of a file from a disk.



