Introduction: 3D Printed Photograph

Picture of 3D Printed Photograph

The 3d printer in our office (an Objet Connex500) prints with a rigid, semitransparent white material that can be used to create these unique black and white photographic prints.  These prints may be indecipherable when viewed from the side, but when backlit with a diffuse light, they recreate images with surprisingly high precision and even add some subtle dimensionality and texture to the scene.

By varying the thickness of a region of this semitransparent print you can control the amount of light that is able to pass through, thereby controlling the brightness (thinner regions of material will appear brighter and thicker regions darker).  In this project, I've converted each individual greyscale pixel of an image to thickness, allowing me to precisely reproduce any greyscale image.  The photos I've printed include an adorable picture my mom took of our cat Teddy (fig 4), Saturn and its moon Titan taken by the Cassini space probe (fig 5 and 6), and a huge print (19x16") of Mt. Williamson by Ansel Adams (fig 1, 2, and 3).

Step 1: The Code

Picture of The Code

All of these 3D models were generated algorithmically from Processing using the ModelBuilder library by Marius Watz. This library allows you to save 3D geometries in the STL file format, STL files that form a watertight mesh can be printed by a 3D printer.

To get started using this code yourself, download the latest version of the ModelBuilder library, unzip the file, and copy the folder into Processing's "libraries" folder. If you have installed the predecessor to the ModelBuilder library (called the Unlekker library), you will need to delete it. Once this is done restart Processing.

//image to 3d printable heightmap/lithophane
//by Amanda Ghassaei
//May 2013

 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
import processing.opengl.*;
import unlekker.util.*;
import unlekker.modelbuilder.*;
import ec.util.*;

String name = "your_file_name_here.jpg";//name of file (with extension - this also works with pngs)

//storage for dimensions
int widthRes;
int heightRes;
float widthDim = 5;//width dimension (in inches)
float widthScaled;
float heightScaled;
float zDim = 0.1;//max vertical displacement (in inches)
float thickness = 0.02;//base thickness (in inches)

boolean invert = true;//if true, then white areas are lower than black, if not true white areas are taller

PImage img;//storage for image
float pixeldata[];//storage for pixel array
UVertexList v1,v2,v3,v4;//storage for verticies
UGeometry geo;//storage for stl geometry

void setup(){
  img = loadImage(name);//load image
  //get dimensions of image
  widthRes = img.width;
  heightRes =img.height;
  size(widthRes,heightRes,P3D);//set dimensions of output
  image(img, 0,0);//display image
  loadPixels();//poad pixels into array
  pixeldata = new float[widthRes*heightRes];//initialize storage for pixel data
  for(int index=0;index<widthRes*heightRes;index++){
    int getPixelData = pixels[index];//get data from pixels[] array
    pixeldata[index] = getPixelData&255;//convert to greyscale byte (0-255)
  //initialize storage for stl
  geo = new UGeometry();
  v1 = new UVertexList();
  v2 = new UVertexList();
  v3 = new UVertexList();
  v4 = new UVertexList();
  //draw stl
    //draw top
    for(int i=0;i<(heightRes-1);i++){
      for(int j=0;j<widthRes;j++){
        widthScaled = j/float(widthRes)*widthDim;
        //top layer
    //draw sides
    for(int j=0;j<widthRes;j++){
      widthScaled = j/float(widthRes)*widthDim;
    //draw sides
    for(int i=0;i<heightRes;i++){
      heightScaled = i/float(widthRes)*widthDim;
        //draw top
    for(int i=0;i<(heightRes-1);i++){
      for(int j=0;j<widthRes;j++){
        widthScaled = j/float(widthRes)*widthDim;
        //top layer
    //draw sides
    for(int j=0;j<widthRes;j++){
      widthScaled = j/float(widthRes)*widthDim;
    //draw sides
    for(int i=0;i<heightRes;i++){
      heightScaled = i/float(widthRes)*widthDim;
  //draw bottom
  //add bottom four corners
  //change extension of file name
  int dotPos = name.lastIndexOf(".");
  if (dotPos > 0)
    name = name.substring(0, dotPos);




Download the latest version of the Processing sketch from GitHub (download as a zip by clicking on the cloud button). Open the folder called Lithograph3DPrint. Copy any greyscale images you want to convert into this folder.

To run the sketch, replace the part in quotes in following line:

String name = "your_file_name_here";

with the name of your greyscale image. I believe .gif, .jpg, .tga, and .png files will all work fine, but I have only tested .jpg so far. Run the sketch, after a minute or two Processing will tell you that it is writing an STL file and eventually it will tell you that it is finished. The resulting file will be located in the sketch's folder named "NAME_OF_ORIGINAL_FILE.stl" You can open the stl file with a variety of CAD software and stl viewers, I like MeshLab for simple viewing (it's free and open source).

By default my sketch will scale images to 8" wide, with a base thickness of 0.02" and feature thickness of up to 0.1", you can change these setting by adjusting the variable at the top of the sketch.


MattO24 made it! (author)2015-11-06

Hey everyone - I noticed some confusion on the installation instructions so I wanted to see if I could help.

On my way over, here are

Install Processing on Your Device

Unzip and Copy the modelbuilder Folder to "Libraries" Folder of Processing

Unzip and Open Lithograph3DPrint-master Folder

    • Doesn't Matter Where
      this Folder is Placed
    • Open Lithograph3DPrint.pde which Opens Processing
      • Upon Opening the File Processing will Migrate the Required File to the Appropriate Sketch Location
        • Keep Using the Folder/File that You Created

Create a Greyscale Image and Place
in Same Folder as Lithograph3DPrint.pde

In Processing Change File Name to Your File
Name In Code

In Processing Change Size to
Height and Width of your File in Code

Click Run

An STL Model will be Generated and Placed within the

henry1973767512 (author)MattO242016-02-29

I dont known how to do it ,I just known a little English =。=

natemathews (author)MattO242016-01-04

I'm lost with this, can anyone help? I followed the instructions and edited the file to include my image file but can't seem to make it run to generate the stl file. I'm on am mac and the file preview shows an exec file.

amandaghassaei (author)MattO242015-11-07

thanks! just pinned this comment to the top.

ymasullo (author)2017-11-10

Did you think perhaps of making a YouTube video so we can all see how to do the process? It would greatly eliminate confusion and allow us all to participate in your terrific creation!

Medelis (author)2016-11-26

just look this cool trick & forget hot bed
Enjoy & happy printing...

caiocallor (author)2016-09-12

Hi there!

I have a problem when running the sketch, it shows ArrayIndexOutOfBoundsException

and error line int getPixelData = pixels[index];//get data from pixels[] array

My photo is 1080x1920. Another one, with 415x415, work one time, but in some others tests, create a STL file that isn´t the picture.

Anyway, awesome work!

RonnieChristmas (author)2016-09-05

Need some help please.. Problem Size/set dimensions

AndrewSparkes (author)2016-05-06

Hello! A really fantastic concept and resource however I'm having some difficulty with the STL file itself as only a flat model is created. No error messages appear.

I've tried both Mac and Windows and tested numerous combinations including increasing RAM allocation. I'm currently using Processing 2.2, Modelbuilder v0007a03 and the lLithograph3DPrinter-Master with the example cat image used in previous tests.

Any help and advice would be gratefully received.

krzys59 (author)2015-09-13

Your code works fine, but with old version of ModelBuilder, with version Mk2 I have errors in this line:

UGeometry geo;//storage for stl geometry

Error is: Cannot find a class type named "UGeometry"

Could you tell me why?

Sorry about my bad English :-)

amandaghassaei (author)krzys592015-09-14

yeah, Mk2 was a big revision, so some of the classes are different. If you're interested in using Mk2 and wanted to learn more about it, you could try to rewrite my code to be compatible - I don't think too much of it would need to change.

ozonostudio (author)2015-05-09

This tutorial needs more instructions, I don't even know what kind of code is this, or how i make this work

smartmiltoys (author)2015-02-24

Very interesting but how are they able to construct a 3D model from a single 2D image?

And in case a 3D tomography imaging how I can create a printed replica.

COJARBI (author)2015-01-10

For some reason the image seem to output in reverse. Also the model seem not to be watertight. Any suggestions?

Jonathan ShilanthaW (author)2015-01-07

hi! im using a makerbot replicator 2, i was able to get the stl file, but when i try to print it on the makerbot the printing does not work. Can anyone help me with this problem .


sydney.fong.3 (author)2014-09-26

Hi ! I am kind of stuck at the beginning when run the sketch.

It shows "No library found for unlekker.util...."

Please help!

Thank you!

alan.phan1 (author)sydney.fong.32014-12-16

First, locate where your Processing sketchbook. This is the default
directory of all of your Processing sketches. On a mac, this will
usually be:


on a PC, it’s probably:

c:/My Documents/Processing/

If it doesn’t already exist, create a folder called “libraries” here. This is where you’ll install the 3rd party libraries.

Copy the extracted files to the Processing libraries folder. Most
libraries you download will automatically unpack with the right
directory structure. The full directory structure should look like this:


More generically:


Some libraries may include additional files in the “library” folder, as
well as the source code (which is commonly stored one directory up, in
the “libraryName” folder). If the library does not automatically unpack
itself with the above directory structure, you can manually create
these folders (using the finder or explorer) and place the
libraryName.jar file in the appropriate location yourself.

Restart Processing. If Processing was running while you
performed above, you will need to quit Processing and restart it in
order for the library to be recognized. Once you have restarted, if
everything has gone according to plan, the library will appear under the
“Sketch → Import Library.”

What to do once you have installed the library really depends on which library you have installed.

JakeR3 (author)2014-11-11

I seem to be having troubles as well. When I run 'Lithograph3DPrint' it just opens a java window and nothing else seems to happen. Any suggestions?

JakeR3 (author)JakeR32014-11-11

And I seem to be getting an eventual "out of memory error".

alan.phan1 (author)JakeR32014-12-16

Goto Preferences and change to memory settings

sobakka_pavlova (author)2014-12-10

Thanks 4 method. But can it somehow use bigger input pictures? Maybe 1000x1000 px. or 2000x2000 for printing LARGE prints, I want to print them cutting stl for parts.

terysocial (author)2014-12-01

Cool. I could print picture easy because of your code. BTW, how could I increase the output dimension of STL? The dimension of my STL is X(6.61), Y(4.99), Z(0.12) in millimeter when the input picture's dimension is 640x480 pixels.

Thank you in advance;)

Shelli_ (author)2014-11-28

Wow this is so cool, will have to try it this weekend! I'm really new to 3D printing, love looking at all the tutorials on here, this article ( is a good primer if anyone wants more of an introduction.

tcambrón (author)2014-11-20

o that is so cool

tcambrón (author)2014-11-20

o that is so cool

davidbarcomb (author)2014-11-19

This is so cool. I would definitely try this

Radioxi (author)2014-03-06

Hello, this program looks excellent! Kudos to you. Unfortunately, every time I try to run the code, Processor tells me I don't have the "unlekker package". You mentioned above that the predecessor was named "unlekker library", which leads me to believe that the version may play some role in this. I downloaded v0007a03 of modbuilder, but perhaps another version would have more success? Any insight would help a lot.

Thanks in advance.

Radioxi (author)Radioxi2014-03-07

Update: I found the unlekker folder, but Processor still refuses to acknowledge its existence.

Radioxi (author)Radioxi2014-03-07

Another Update: Figured it out; the modelbuilder folder wasn't registering as a library because it was in a replicate folder with the same name

amandaghassaei (author)Radioxi2014-03-08

great! did you get a model out?

Radioxi (author)amandaghassaei2014-03-10

Yeah, works like a charm now.

pandaoso (author)Radioxi2014-11-12

hello, can you help me to know how you did it, can you explain me step by step thanks

cadsage (author)Radioxi2014-04-06

Have a question for you- finally got the above problem figured out (thanks for your post!), but now it is giving me the message "could not find a method to load ___.jpg" Help!!!!

langbatkyho made it! (author)2014-10-29

Fantastic! I made it and that's such ease to turn a 2D portrait of my child to 3D sketch. Thanks

giladaya (author)2013-05-22

This is so cool!
The results look surprisingly good, rich with details.

I would use this technique to make a pendant that's smooth and plain on one side, but reveals an image when held against the light.

giladaya (author)giladaya2014-09-15

It took me some time but I ended up implementing a service for creating such pendants. If anyone is interested it's available at

The generated models can be downloaded for printing at home.

gravityisweak (author)giladaya2014-10-06

Wow! Thanks for taking the time to do this! When I tried it though, it just seemed to hang on the Redirecting part forever...

giladaya (author)gravityisweak2014-10-06

Thanks for trying it and for the feedback!

I'm aware of this problem and working on a solution, it should be fixed in a day or two...

omer.g.gassi (author)giladaya2014-09-16

Great site, gilad, thank you for sharing.

gravityisweak (author)2014-10-06

This is really incredible. I've finally got my hands on a 3d printer and plan to give this a try ASAP!

omer.g.gassi (author)2014-09-15

Thank you Amanda, iv'e been looking to try this method for a long time.

I'm new to this Github things so i will probably ask a lot of dumb questions.

1: after i download the "lithograph3DPrint file" where do i save it? is it inside the modelbuilder library?

2: how can i change the lines "String name = "your_file_name_here"" its seems unavailable.

JanH2 (author)2014-09-13

Firstly, thank you amandaghassaei for excelent job! I love your method of creating lithophanes.

There are only two things I would like to ask how to solve:

1) The bottom two faces seems to be somehow corrupted? maybe wrong normal or I don't know - Meshmixer shows it and Repetier also stuck loading the file (
2) Is there any way how to get more smooth result of (e.g vector graphic) lines, directly from Processing? because printer then print the perimeters with lot of noise. (

mombott (author)2013-05-23

This would make fantastic inserts in place of the glass used in kitchen cabinet doors. I'd do a series of fruit and veg pictures from 'old masters' and back light them.

realguy (author)mombott2014-05-18

great idea! The whole idea of 2D to 3D for similar purposes (faux leaded glass!)

A 3D printer is next when I can justify getting it and writing off for printing, um, 3D…uh, business documents!!!

Echochrome3 (author)2014-04-17

I have never really used processing, and so I don't know what alot of this means. So could you help fix the problem?

Sounds like the image isn't loading, did you put it in the same folder as the code?

Where would that be, you said to download the file from Github I then extracted the folder to my desktop. I opened the folder opened the sketch, it said it need to make its folder for the sketch and then it did. I put the image in there, and click run. It gives me the error you see and highlights widthRes = img.width; saying could not find method to load kitten.

can you post the image and the code you're running?

About This Instructable




Bio: I'm a grad student at the Center for Bits and Atoms at MIT Media Lab. Before that I worked at Instructables, writing code for ... More »
More by amandaghassaei:OTCA Metapixel - Conway's Game of Life"9 Degrees of Freedom" IMUTwitter Controlled Pet Feeder
Add instructable to: